fix(sdk): preserve contract errors through safe() and isDefinedError - #1692
Conversation
Separate-session Fable 5 medium evaluation of PR #1671 plan head 2fa2f71. Verdict PASS with five non-blocking advisories. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015RuDy1h3UiCkLzo1PLk5Sc
…correction map Maps all 13 new leaf-owned deno-doc-lint private-type-ref findings from S4 (contracts 3, SDK 10) to individual type-safe corrections, verified against isolated deno doc --lint probes and the real @orpc/* .d.ts files. 12 of 13 resolve cleanly (SDK 10/10; contracts BaseContractErrors + Schema); one (baseContract -> ContractBuilder) is reported unresolved pending a coordinator ruling rather than planned around. Run-artifact-only: no product/test/docs/lock file touched. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TYBPuyVoK8Bc8926DfnPah
…replaced by #1692; S6 dispatched Records plainly that merging #1691 closed #1671 because the body I wrote contained the literal token 'close #1671' inside a sentence disclaiming it - GitHub matches the token and does not read negation. Lesson: never write the literal closing token even to deny it. Rebase onto 61bfd85 replayed all ten commits with zero conflicts and proven byte-identical leaf content. #1692 opened as the replacement at 9cdba63. Gates re-executed at the new head all green. The S6 corpus precondition now PASSES - 0 added/removed exports, 5 changed signatures all @netscript/sdk and all already approved - so S6 was dispatched to the same author thread. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XmfcnZVCo7NfkhWBuToAiV
|
[PHASE: IMPL] S6 regenerated the derived MCP export corpus for the five approved SDK signature changes and stopped at pushed head Scope
Structured evidence{
"gateId": "mcp-export-corpus-determinism",
"outcome": "PASS",
"exitCode": 0,
"runs": 2,
"byteIdentical": true,
"generatedFileSha256": "f7bbc8925481e8682f84f9057263387030838e6bc7ee366c56e98a9b2829f904",
"embeddedCorpusSha256": "a8f0779228987ed7e304dc032d45d1488b0cfb651b088d563c1e17fbafa2fb0b"
}{
"gateId": "mcp-export-corpus-semantic-delta",
"outcome": "PASS",
"schemaVersionUnchanged": true,
"frameworkVersionUnchanged": true,
"surfacesUnchanged": true,
"addedExports": 0,
"removedExports": 0,
"changedSignatureCount": 5,
"changedSignatures": [
"@netscript/sdk:.#SafeFailure",
"@netscript/sdk:.#SafeResult",
"@netscript/sdk:.#ServiceClientMethod",
"@netscript/sdk:.#isDefinedError",
"@netscript/sdk:.#safe"
]
}{
"gateId": "check:mcp-export-corpus",
"gitHead": "b427e035488e5eabd9f3a92870787006aa9a6813",
"actualGitHead": "b427e035488e5eabd9f3a92870787006aa9a6813",
"waiver": null,
"outcome": "PASS",
"exitCode": 0,
"packageCount": 35,
"subpathCount": 270,
"symbolCount": 7611
}{
"gitHead": "b427e035488e5eabd9f3a92870787006aa9a6813",
"actualGitHead": "b427e035488e5eabd9f3a92870787006aa9a6813",
"waiver": null,
"gates": [
{
"gateId": "mcp-scoped-lint",
"outcome": "RED_PRE_EXISTING_TOOLING",
"exitCode": 1,
"findings": 0,
"failure": "Failed to parse workspace configuration"
},
{
"gateId": "mcp-scoped-fmt",
"outcome": "RED_PRE_EXISTING_TOOLING",
"exitCode": 1,
"findings": 0,
"failure": "Failed to parse workspace configuration"
}
]
}The lint/format wrappers reproduce the accepted exit-1/zero-findings tooling red. Their local structured detail is an early workspace-configuration parse failure, not a source finding, and no suppression or source change was attempted. Next
|
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XmfcnZVCo7NfkhWBuToAiV
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
[PHASE: IMPL-EVAL] [VERDICT: PASS] Evaluated head: Evaluator: Claude Fable 5 · medium, fresh native session, own detached worktree. Generator: Codex gpt-5.6-sol (thread 01a006f3). Product content: Disposition of findings
Review trail: IMPL-EVAL at Scope of this evaluator: no merge, labels, readiness, checkboxes, #1348/#1466 mutation, or runtime lease were touched at any step. Original IMPL-EVAL at bcc9f39 — PASS-WITH-FINDINGS (verbatim, unedited)IMPL-EVAL — PR #1692 (#1350
|
| Field | Value |
|---|---|
| Head evaluated | bcc9f393d993cd5468015c883c8b0dc6a5b6dc62 |
| Base | main@61bfd858d20f3bf61e7ee45b5646537af567f247 (merge-base confirmed equal) |
| Evaluator | Claude Fable 5 · medium, fresh native session, worktree netscript-007-eval-1692 |
| Generator | Codex gpt-5.6-sol (opposite family) |
| Base control | detached worktree /tmp/ns-eval-base at the base SHA (read-only; removed afterwards) |
| Archetype | Archetype 1 — small contract, docs overlay |
Verdict
PASS-WITH-FINDINGS (harness vocabulary: PASS; no FAIL_* condition is met).
Blocking for a later status:ready-merge flip, not for this draft head: F1 (breaking-change
disclosure is absent from every consumer-visible record — PR body, commit messages, docs pages).
Everything else is non-blocking.
Head identity
git rev-parse HEAD → bcc9f393d993cd5468015c883c8b0dc6a5b6dc62
git ls-remote origin fix/sdk-typed-error-channel → bcc9f393d993cd5468015c883c8b0dc6a5b6dc62
gh pr view 1692 --json headRefOid → bcc9f393d993cd5468015c883c8b0dc6a5b6dc62
git merge-base HEAD 61bfd858d → 61bfd858d20f3bf61e7ee45b5646537af567f247
Diff stat vs base: 15 paths — 8 run artifacts, 4 source/test, 1 generated corpus, 2 docs pages.
packages/contracts/src/public/mod.ts is not in the diff (claim 3 holds).
Claims tested
1. Six-code union preserved; assertion non-vacuous — CONFIRMED
Codes re-derived from source (awk over the commonErrorMap literal in
contract-primitives.ts, not from the test's constant):
NOT_FOUND|VALIDATION_ERROR|UNAUTHORIZED|FORBIDDEN|RATE_LIMITED|SERVICE_UNAVAILABLE.
Independent probe (my own file, imports @netscript/contracts + packages/sdk/mod.ts, asserts
Equal<keyof baseContract['~orpc']['errorMap'], Src>, IsAny=false, [never]=false, and the same
union on safe().error.code and isDefinedError()-narrowed error.code):
| Run | Result |
|---|---|
| head, probe | deno check exit 0 |
| head, probe with one code removed | TS2344 Type 'false' does not satisfy 'true', exit 1 |
| base worktree, same probe | TS2344 + TS2571 unknown + TS2339 'code' on never, exit 1 |
The base failure texts are exactly the two RED texts PLAN-EVAL advisory A1 predicted.
2. Doc-lint at exact baseline parity — CONFIRMED
deno run --allow-read --allow-write --allow-run .llm/tools/run-deno-doc-lint.ts --root packages/<p>:
| Package | head totalPrivateTypeRef |
base totalPrivateTypeRef |
|---|---|---|
| contracts | 9 | 9 |
| sdk | 3 | 3 |
Raw deno doc --lint packages/contracts/mod.ts at head: the only baseContract diagnostic is
references private type 'oc' (contract-primitives.ts:120:14). sdk's 3 are QueryClient refs,
pre-existing.
3. No public-barrel growth — CONFIRMED
git diff --stat 61bfd858d..HEAD does not list packages/contracts/src/public/mod.ts.
deno task docs:exports-drift → Exports & Symbols drift check: PASS, exit 0.
4. Corpus delta leaf-owned only — CONFIRMED (decoded, not file-diffed)
Decoded both gzip/base64 corpora (7611 entries each) and compared on
(packageName, subpath, symbol, kind):
ADDED [] REMOVED [] CHANGED 5
@netscript/sdk . SafeFailure typeAlias signature
@netscript/sdk . SafeResult typeAlias signature
@netscript/sdk . ServiceClientMethod typeAlias signature
@netscript/sdk . isDefinedError function signature
@netscript/sdk . safe function signature
surfaces equal: True frameworkVersion 0.0.6 == 0.0.6
deno task gen:mcp-export-corpus re-run at head: decoded output identical to the committed
artifact (regen decoded == committed decoded: True); file restored afterwards.
5. Breaking-change disclosure at full strength — PARTIAL → F1
The surface:diff 532 → 531 signal is recorded as a tooling false negative, not banked:
worklog.md:1366-1368 ("drops the instantiation argument, so the signal is a known tooling false
negative"). Independently confirmed: the decoded corpus renders baseContract as
const baseContract: ReturnType<oc.errors> at head — identical to the base rendering — so the tool
cannot see the change.
The strength problem is location. The breaking verdict exists only in harness artifacts
(plan.md §"Breaking-change verdict", worklog.md S4/S5 sections). Executed searches:
| Record | Grep for break|undefined|null|migrat|major|semver | Hit |
| -------------------------------------------------------------- | ---------------------------------------------------- | --- |
| PR #1692 body (gh pr view 1692 --json body) | none | 0 |
| 12 leaf commit messages + bodies (git log 61bfd858d..HEAD) | none; no ! or BREAKING CHANGE footer | 0 |
| docs/site/services-sdk/sdk.md, how-to/discover-services.md | none | 0 |
| sole PR comment (2026-08-23T08:34:38Z) | only "waiver": null in JSON | 0 |
The PR carries type:fix, the commits are fix(sdk)/fix(contracts), and the docs pages replace the
old const [error, result] = await safe(...) idiom with the new result.isSuccess/isDefined idiom
without saying the old shape (data: null, single isDefined: boolean arm, TError = unknown)
is gone. plan.md:251 requires "Declare breaking change explicitly … document migration".
6. ThrowableError → Error — ACCEPTABLE, recorded
grep -rn "declare module '@orpc/shared'|throwableError" packages plugins → 0 hits; Registry
is un-augmented, so ThrowableError ≡ Error today. "Leaf-new" holds in the sense that matters:
at base the published default was TError = unknown; ThrowableError was only ever a plan-level
choice (PLAN-EVAL A3), never published, so no JSR consumer can depend on it. Moving Error → ThrowableError later is additive for every consumer that has not augmented Registry. Recorded as
a declared design decision in worklog.md:1166-1170. No finding beyond F3 (no follow-up pointer).
7. Bounded couplings (__error?: { type: E } inlined) — ACCEPTABLE
Inlined shape is Promise<T> & { __error?: { type: E } }, duplicated in errors.ts and
service-client.ts. Judgement: this is a two-property phantom marker, not a reconstructed class —
the asymmetry with rejecting a local ContractBuilder reconstruction (many generics + method
surface) is defensible and does not meet AP-1/AP-9. Drift behaviour if oRPC renames the marker:
TError inference silently degrades to the Error default — but the in-tree
readme-doctest_test.ts Equal<typeof discriminated.error.code, ExpectedBaseErrorCode> assertion
turns TS2344, and claim-1 probe above shows that assertion is live. The drift risk is therefore
detected by a gate, not only "named".
Gates executed at head (structured wrappers; raw exit codes)
| Gate | Result | Exit |
|---|---|---|
run-deno-check.ts --root packages/sdk --root packages/contracts |
105 files, 0 findings | 0 |
run-deno-check.ts --root packages/fresh (only out-of-leaf consumer of isDefinedError) |
197 files, 0 findings | 0 |
run-deno-test.ts -- --allow-all packages/sdk/tests packages/contracts |
77 passed / 0 failed | 0 |
run-deno-lint.ts (sdk+contracts) |
0 findings | 0 |
run-deno-fmt.ts --ext ts,tsx (sdk+contracts) |
0 findings | 0 |
deno publish --dry-run --allow-dirty in packages/contracts |
Dry run complete | 0 |
deno publish --dry-run --allow-dirty in packages/sdk |
Dry run complete | 0 |
deno task docs:exports-drift |
PASS | 0 |
run-deno-doc-lint.ts contracts / sdk, head vs base |
9/9, 3/3 | — |
gen:mcp-export-corpus regen vs committed (decoded) |
identical | 0 |
Known pre-existing reds (packages/mcp fmt/lint batch, surface:diff ~531 majors, F-DOCT-5) were
not re-run and are not attributed to the leaf.
Process checks
- PLAN-EVAL:
plan-eval.mdverdictPASS(commite78f87b12) precedes the first
implementation commit5d348fbc8in ancestry. OK. - Design checkpoint:
worklog.md§"Design" (public surface, vocabulary, ports, constants, slices,
deferred scope, contributor path). Slices S1–S6 match plan "Commit slices". OK. - Out-of-leaf consumers: only
packages/fresh/src/diagnostics/error/extract.tsimports
isDefinedError; type-checks clean. No repo source assertsSafeFailure.data === null. - arch-debt delta: none (no doctrine violation introduced; see F5 for the A4 advisory).
- Close-gate: PR is draft; [sdk-client S1] fix(sdk): preserve contract errors through safe() and isDefinedError #1350 boxes not assessed for ticking (out of this pass's remit).
## SKILLchapters: the run dir stores no agent briefs, so this cannot be verified from artifacts
(grep -c "## SKILL" .llm/runs/…/*.md→ 0 in every file). Recorded as unverifiable, not as a
finding against the generator.
Findings
| ID | Severity | Finding | Evidence |
|---|---|---|---|
| F1 | Medium (blocks ready-merge, not this draft) | Breaking-change disclosure exists only in plan.md/worklog.md. PR body, all 12 commit messages, and both docs pages contain no breaking/migration statement; PR is labelled type:fix, commits are fix(...) with no !/BREAKING CHANGE. plan.md:251 requires explicit declaration + migration notes. |
greps in §5 above; gh pr view 1692 --json body |
| F2 | Low | safe() parameter narrowed from PromiseLike<TOutput> to Promise<TOutput> & { __error?: … }; non-Promise thenables are now rejected. Not enumerated anywhere as a consumer-visible break (only the base signature is quoted at plan-eval.md:97). |
probe declare const p: PromiseLike<number>; safe(p) → TS2345 … not assignable to 'Promise<number> & { __error?: …}', exit 1 |
| F3 | Low | ThrowableError → Error foreclosure is recorded in worklog.md:1166-1170 but has no follow-up pointer (issue/debt) for the day Registry.throwableError augmentation becomes wanted. |
grep -rn throwableError packages plugins → 0; worklog lines cited |
| F4 | Low | For an untyped Promise<T> (TError defaults to Error), the isDefined: true arm types error as never (Extract<Error, DefinedErrorLike>), while createSafeFailure can still return isDefined: true at runtime if an ORPCError with defined: true rejects that promise. Typed-unreachable, runtime-reachable branch. The leaf's own _PlainErrorRejectedFromDefinedArm asserts this typing deliberately and sdk.md documents the intent; noting it because it is a behavioural difference from the base's isDefined: boolean arm. |
errors.ts:141-148 (runtime); readme-doctest_test.ts _PlainErrorRejectedFromDefinedArm |
| F5 | Low | PLAN-EVAL advisory A4 asked for an arch-debt.md/issue entry so the bench-prose follow-up is literally "tracked". drift.md:35-36 states "No new file or debt entry was created; the coordinator owns any later issue" while drift.md:25 still says "remains tracked follow-up debt". Not a doctrine violation by this leaf, so not FAIL_DEBT; the word "tracked" is currently unbacked. |
drift.md:25,35-36; git diff --stat shows no debt file change |
| F6 | Info | surface:diff 532→531 on baseContract is a deno doc rendering loss (ReturnType<oc.errors> at both base and head in the decoded corpus). Correctly recorded as a false negative in worklog.md:1366-1368; must not be cited as a clean surface result at cut time. |
corpus decode above |
What I did not do
No product, test, docs, or label mutation. No merge, readiness flip, checkbox, #1348/#1466 change,
or runtime lease. Scratch probes were created under packages/sdk/tests/__eval1692/ and removed;
git status is clean apart from this artifact. /tmp/ns-eval-base worktree removed after use.
…f repaired Fable 5 medium evaluator confirmed six of seven claims with executed evidence, including a three-way control stronger than this topic's own. Claim 5 became F1: breaking-change disclosure existed only in harness artifacts. F1's PR-body half was mine - I wrote #1692's replacement body and omitted the disclosure - and is now repaired with a full breaking table, migration notes, and the explicit statement that surface:diff must not be cited as corroboration. F2 is enumerated there too. Commit messages and docs pages remain unrepaired and need a coordinator ruling; F3/F4/F5 open with recommendations. Readiness not flipped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XmfcnZVCo7NfkhWBuToAiV
Document the consumer migration path and the bare-Promise defined-arm characteristic. Link the deferred prose and ThrowableError substitution follow-up. BREAKING CHANGE: SafeFailure/SafeResult failure payload changes null -> undefined, default TError changes unknown -> Error, and safe() no longer accepts non-Promise thenables. Pre-1.0 intentional break; not patch-compatible. See PR #1692. Refs #1693
|
[PHASE: IMPL] S7 closes the consumer-documentation half of IMPL-EVAL finding F1 and stops at pushed head Scope and disclosure
Structured evidence{
"gateId": "docs:snippets",
"gitHead": "29c9e40aad391381e79afa92a6052cbcd07d9a4a",
"actualGitHead": "29c9e40aad391381e79afa92a6052cbcd07d9a4a",
"outcome": "PASS",
"exitCode": 0,
"scanned": 581,
"tier1": 36,
"checked": 22,
"exempt": 14,
"malformed": 0
}{
"gateId": "docs-source-format",
"gitHead": "29c9e40aad391381e79afa92a6052cbcd07d9a4a",
"actualGitHead": "29c9e40aad391381e79afa92a6052cbcd07d9a4a",
"waiver": null,
"outcome": "PASS",
"exitCode": 0,
"durationMs": 223,
"requestHash": "b92bd2a4471968dd3d58219ab2200a368a2fca2703fce03206a12105d23142cf"
}{
"gateId": "docs-accuracy",
"gitHead": "29c9e40aad391381e79afa92a6052cbcd07d9a4a",
"actualGitHead": "29c9e40aad391381e79afa92a6052cbcd07d9a4a",
"waiver": null,
"outcome": "PASS",
"exitCode": 0,
"durationMs": 8640,
"requestHash": "743dcd4a345037ec14c817c7f3bed19febdcf2a664570e9bdab2fc1dc8ff5cd6",
"existingNonBlockingWarning": "@tanstack/ai-preact peer @tanstack/ai constraint mismatch"
}{
"gates": [
{
"gateId": "docs:exports-drift",
"outcome": "PASS",
"exitCode": 0
},
{
"gateId": "check:mcp-export-corpus",
"outcome": "PASS",
"exitCode": 0,
"unchanged": true,
"sha256": "a8f0779228987ed7e304dc032d45d1488b0cfb651b088d563c1e17fbafa2fb0b"
},
{
"gateId": "s7-packages-scope",
"command": "git diff --name-only 1772dfdf9f26a9c7ed76f196e93505732696fb30..HEAD -- packages/",
"outcome": "PASS",
"exitCode": 0,
"paths": []
}
],
"denoLock": "BYTE_IDENTICAL",
"exportCorpusGeneratedFile": "BYTE_IDENTICAL"
}
Next
|
Amendment review (F1/F3/F4/F5 closure) — ACCEPT-WITH-FINDINGSHead reviewed: Full review (verbatim artifact)Amendment review — PR #1692, F1/F3/F4/F5 closure
VerdictACCEPT-WITH-FINDINGS. F1 is closed to the standard Head and scope verificationThe product tree is byte-identical to the evaluated S6 head, so the original product gate results Receipts re-run at this head
Markdown fmt of Item-by-item1. F1 closure — CLOSED, with two under-claimsBoth pages carry a "Migrating … to 0.0.7" section (
Over-claim check: both pages state "The tuple form has not been removed … destructuring still PR body: " A consumer reading only these pages is warned about the five listed surfaces. They are not warned 2. F4 closure — CLOSED, one precision defect
3. F3 + F5 closure — CLOSED
4. Commit hygiene — SUFFICIENT
Findings (amendment text only)
Not doneNo product, test, or docs edits; no merge, label, checkbox, readiness, #1348/#1466 mutation, or |
Add the SafeFailure literal-arm and baseContract key-space breaks, correct the historical safe signature, and express bare-Promise behavior only through exported SDK vocabulary. BREAKING CHANGE: SafeFailure now has literal defined and non-defined arms, and baseContract rejects error codes outside its six declared literals. This extends the documented 0.0.7 pre-1.0 migration; it is not patch-compatible. See PR #1692. Refs #1693
|
[PHASE: IMPL] S8 — amendment-review prose corrections A1–A4 S8 is complete and pushed. Exact branch head: Corrections:
The exact sentence “The tuple form has not been removed” remains on both pages. The docs commit is Structured verdicts at {"gateId":"check:mcp-export-corpus","outcome":"PASS","exitCode":0,"unchanged":true,"sha256":"a8f0779228987ed7e304dc032d45d1488b0cfb651b088d563c1e17fbafa2fb0b","packageCount":35,"subpathCount":270,"symbolCount":7611}
{"gateId":"s8-packages-plugins-scope","outcome":"PASS","exitCode":0,"paths":[],"denoLock":"BYTE_IDENTICAL","exportCorpusGeneratedFile":"BYTE_IDENTICAL"}
{"gateId":"docs:snippets","outcome":"PASS","exitCode":0,"scanned":581,"tier1":36,"checked":22,"exempt":14,"malformed":0}
{"gateId":"docs:accuracy","outcome":"PASS","exitCode":0,"invocationId":"sdk-typed-error-s8-docs-accuracy","durationMs":8275,"requestHash":"248e287e45996f0342949331ff5c4dc2bc3869f435fd6e5e4f49dbf6ba62a36e","summary":"199 published source pages; 91/91 root/direct public commands from 149 recursive paths"}
{"gateId":"docs:links","outcome":"PASS","exitCode":0,"docs":103,"brokenLinks":0,"brokenAnchors":0,"orphans":0}
{"gateId":"docs:exports-drift","outcome":"PASS","exitCode":0,"summary":"Exports & Symbols drift check: PASS"}
Base-to-head mutation is exactly the two authorized docs pages plus existing Stopping for the coordinator-owned opposite-family A1–A4 delta re-review. |
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Amendment delta review (A1–A4) — PASSHead reviewed: Full delta review (verbatim artifact)Amendment review delta — PR #1692, A1–A4 prose repair (S8)
VerdictPASS on A1, A2, A3, A4. No over-claim or under-claim found in the corrected text. No new finding. Head and scopeReceipts re-run at this head, all exit 0: A1 —
|
…ent rewritten Delta re-review terminal PASS on A1-A4. All receipts re-executed at the exact final head rather than carried over. PR #1692 body rewritten completely and verified live: exactly one closing keyword (Closes #1350), 9/9 DoD checked, 7 acceptance BOX entries citing real comment ids, zero stale head references. Includes A4's second half, which was this topic's to fix - base isDefinedError returned error is Extract<T, DefinedError>. #1671 comment 5304357008 rewritten in place: it pinned a rebased-away head, recorded the refuted barrel-exposure ruling as adopted, and listed now-green gates as NOT_RUN. Original preserved beneath a divider. Evaluator asked to rewrite its own IMPL-EVAL comment in place, with the closing-token trap stated explicitly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XmfcnZVCo7NfkhWBuToAiV
S7 and S8 changed the two SDK documentation pages consumed by the agent-docs prose bundle. Regenerate the canonical prose, provenance, CLI barrel, and publish assets; every changed file is generator output.
|
[PHASE: IMPL] S9 — regenerate the agent-docs generated cascade S9 is complete and pushed. Exact branch head: The coordinator-specified cascade ran twice in dependency order with no hand edits:
Measured changed paths:
Determinism proof: {"path":".llm/assets/agent-docs/prose.json.gz","pass1":"5082cf83b11ddfe64ac26f1c37c719074c55e244382ade26d310861b53348df0","pass2":"5082cf83b11ddfe64ac26f1c37c719074c55e244382ade26d310861b53348df0"}
{"path":".llm/assets/agent-docs/provenance.json","pass1":"fee682e73c243a207fd8e83557d5a96a62acf95f7ec1f3c14294e3908289e8b7","pass2":"fee682e73c243a207fd8e83557d5a96a62acf95f7ec1f3c14294e3908289e8b7"}
{"path":"packages/cli/src/kernel/assets/agent-docs.generated.ts","pass1":"b838cd7505b10ba0f24c0da3c8836ceed1a9ab1e975168d1fc0bbdd20b05246a","pass2":"b838cd7505b10ba0f24c0da3c8836ceed1a9ab1e975168d1fc0bbdd20b05246a"}
{"path":"packages/mcp/src/publish-assets.generated.ts","pass1":"5fec4b20254a3fa9fa7d1f0dad4bdad10b00d41e9737da96711a2956f5ca90c3","pass2":"5fec4b20254a3fa9fa7d1f0dad4bdad10b00d41e9737da96711a2956f5ca90c3"}Structured verdicts at {"gateId":"agent-docs-prose","invocationId":"sdk-typed-error-s9-agent-docs-prose","outcome":"PASS","exitCode":0,"durationMs":12160,"requestHash":"9c24d015c0c7d72d724e0e4010be07cf3c7faae01ccbdd6c2c57e2d0549275a8","fresh":true,"stalePaths":[]}
{"gateId":"assets-barrel","invocationId":"sdk-typed-error-s9-assets-barrel","outcome":"PASS","exitCode":0,"durationMs":717,"requestHash":"57ec4456cfbca0a889aba6c5d395bf015da244e36140a98b93871567ee4c5a39"}
{"gateId":"publish-assets","invocationId":"sdk-typed-error-s9-publish-assets","outcome":"PASS","exitCode":0,"durationMs":315,"requestHash":"a10cf114c57501fc9d391b35a009d7beca76250784c361472f77f6ac0bf17f8b"}
{"gateId":"check:mcp-export-corpus","outcome":"PASS","exitCode":0,"unchanged":true,"sha256":"a8f0779228987ed7e304dc032d45d1488b0cfb651b088d563c1e17fbafa2fb0b","generatedFileSha256":"f7bbc8925481e8682f84f9057263387030838e6bc7ee366c56e98a9b2829f904","packageCount":35,"subpathCount":270,"symbolCount":7611}
{"gateId":"docs:exports-drift","outcome":"PASS","exitCode":0,"summary":"Exports & Symbols drift check: PASS"}
{"gateId":"contracts-sdk-tests","outcome":"PASS","exitCode":0,"durationMs":5601,"passed":78,"failed":0,"ignored":0,"totalResults":78}
At S9 start PR #1692 was externally observed as ready with sole Stopping for separate review and coordinator-owned CI/readiness handling. |
Merged 2026-08-23T09:58:53Z as c73d361 from source head 686bae0. origin/main verified at the merge commit. #1350 CLOSED/ COMPLETED with 7/7 boxes; both #1350 and #1692 reconciled to a single status:shipped label. Keeps five lane lessons for the next run, including that a negated closing keyword still closes a PR, and that two consecutive grep-precision near-misses would each have filed a false finding against a correct author. Worktrees and branches intentionally left in place - coordinator owns cleanup. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XmfcnZVCo7NfkhWBuToAiV
Closes #1350
Preserves NetScript's exact six-code contract error union through
safe()andisDefinedError(), andremoves this leaf's own
private-type-refregressions from the published surface.Supersedes PR #1671 — same branch, same author thread, same work. See Provenance.
Published surface in
@netscript/sdkand@netscript/contractschanges. Thetype:fixlabel and thefix(...)prefixes on the early commits do not soften this; the disclosure commits carrydocs(sdk)!with
BREAKING CHANGE:footers.SafeFailure/SafeResultfailure payloaddatawerenullundefinedSafeFailurearmsisDefined: booleanisDefined: false/isDefined: trueSafeFailure/SafeResultdefaultedTErrortounknown;safe<TOutput>had noTErrorparameter and inherited that defaultTErrortoErrorServiceClientMethod<TInput, TOutput>returningPromise<TOutput><TInput, TOutput, TError = Error>returningPromise<TOutput> & { __error?: { type: TError } }; the phantom marker is what letssafe()recoverTErrorsafe(promise)inputPromiseLike<TOutput>Promise<TOutput> & { __error?: … }— non-Promisethenables now failTS2345; wrap withPromise.resolve(...)isDefinedErrorreturnerror is Extract<T, DefinedError>Extract<…> & DefinedErrorbaseContracterror-map key spaceerror.codeto an undeclared code is now a type errorMigration. Change
failure.data === nulltofailure.data === undefined, or prefer thediscriminated form (
result.isSuccess, thenresult.isDefined). Branch on the literal rather thantyping
isDefinedasboolean. Wrap non-Promisethenables. Annotate explicitly if you relied onTError = unknown. The tuple form is not removed — both arms remain tuple-and-objectintersections, so destructuring still works.
surface:diffdoes not corroborate this and must not be cited as a clean surface result:deno docdrops the instantiation argument from the new
baseContractannotation, so the tool stopped reportingits signature change (undeclared majors 532 → 531). That is a tooling false negative.
Scope at head
686bae07b2bc66353b2eec9dd56baa0779a63a20Four source/test paths, one derived artifact, two docs pages, plus run artifacts:
packages/contracts/src/application/contract-primitives.tspackages/sdk/src/client/errors.tspackages/sdk/src/ports/service-client.tspackages/sdk/tests/readme-doctest_test.tspackages/mcp/src/infrastructure/export-surfaces/export-surface-corpus.generated.ts(regenerated; delta is 5@netscript/sdksignature changes, 0 added/removed exports)docs/site/services-sdk/sdk.md,docs/site/services-sdk/how-to/discover-services.mdpackages/contracts/src/public/mod.tsis not touched. No metadata vocabulary, no lint suppressions,no
docs/site/reference/page.The
baseContractannotationReturnType<typeof oc.errors>collapses the type parameter to itsErrorMapupper bound and erases thesix literal codes — the defect this issue exists to repair. A
ContractBuilder<…>annotation preservedthem but put three oRPC private types into a published signature. This uses a TypeScript instantiation
expression,
ReturnType<typeof oc.errors<{…exact map…}>>, which keeps the parameter instantiated sothe six codes survive while naming no oRPC builder type;
ContractBuilderis no longer imported.Exposing
ContractBuilder/Schema/BaseContractErrorsfrom the public barrel was measured andwithdrawn: it takes
packages/contracts/mod.tsfrom 10 to 21private-type-refdiagnostics andturns
docs:exports-driftred.Prerequisite #1691 (merged)
check:mcp-export-corpuswas already red onmain@9634735bc0before this branch existed. Regeneratinginside this PR would have made it the carrier for 9 exports belonging to
@netscript/aiand@netscript/prisma-adapter-mysql. That repair landed separately as #1691(
61bfd858d20f3bf61e7ee45b5646537af567f247); this branch is rebased onto it and the rebase introduced nocontent change.
Definition of Done
private-type-refdiagnostics; both packages at exact baseline parity!/BREAKING CHANGEcommit footersThrowableErrordecision + bench-prose debt)Acceptance evidence — all seven #1350 boxes
Provenance
PR #1671 carried slices S1–S5 and was closed unmerged at
2026-08-23T08:18:03Zas an unintended sideeffect of merging #1691, whose body contained a literal closing keyword token inside a sentence
disclaiming one — GitHub's parser matches the token and does not read negation. #1671 was never merged.
Reopening was refused because the branch had since been rebased. No work was lost: same branch, same
commit history, same author thread.
Status
Draft. Not ready for merge: readiness, labels, and issue checkboxes are the coordinator's to flip.