Skip to content

v11.0.0: Hardening Sprint#30

Merged
flyingrobots merged 15 commits intomainfrom
hardening-sprint
Feb 15, 2026
Merged

v11.0.0: Hardening Sprint#30
flyingrobots merged 15 commits intomainfrom
hardening-sprint

Conversation

@flyingrobots
Copy link
Member

@flyingrobots flyingrobots commented Feb 15, 2026

Summary

  • BREAKING: autoMaterialize now defaults to true — query methods transparently call materialize() when no cached state exists. Pass autoMaterialize: false to preserve old behavior.
  • B1: Writer whitelist for sync ingress — allowedWriters option on HttpSyncServer rejects unlisted writer IDs with HTTP 403
  • B8: refExists() / readRef() handle dangling-ref exit 128, returning null/false instead of throwing
  • B9: End-to-end graph.patch() CAS integration tests with real Git persistence
  • B10: Writer.commitPatch() reentrancy guard matching graph.patch() semantics
  • M1.T2: Dependency risk assessment, accepted risks, threat model boundaries in SECURITY.md; npm audit CI gate
  • M2.T3: Improved E_NO_STATE / E_STALE_STATE messages with recovery hints and docs URLs; migration guide in GUIDE.md

Test plan

  • npm run lint — clean
  • npm run typecheck — clean
  • Unit tests — 174 files, 3589 tests passed
  • Integration tests — 11 files, 59 tests passed
  • git warp info smoke test — works
  • CI matrix (node22, bun, deno)

Summary by CodeRabbit

  • Breaking Changes

    • autoMaterialize now defaults to true; migration guidance provided.
  • New Features

    • Optional writer whitelist for sync endpoints (configurable enforcement/log-only).
    • Reentrancy guard for commit/patch workflows.
    • Safer handling of dangling Git refs and related resilience.
  • Documentation

    • Added SECURITY guidance; updated GUIDE, README, ROADMAP and CHANGELOG with migration notes.
  • Chores

    • CI: added non-failing security-audit step.
  • Tests

    • New integration and unit tests for patching, auth, adapter, and reentrancy.
  • Release

    • Version bumped to 11.0.0.

CI Bot added 3 commits February 14, 2026 22:16
…angling-ref resilience

BREAKING CHANGE: autoMaterialize now defaults to true. Pass autoMaterialize: false to preserve old behavior.

Completes M1.T2, M2.T3, B1, B8, B9, B10. Bumps to v10.15.0.
Add JSDoc type annotations for allowedWriters param, narrow union types
in SyncAuthService.verifyWriters, and cast materialize() returns in
integration tests.
@coderabbitai
Copy link

coderabbitai bot commented Feb 15, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

Defaults autoMaterialize to true; adds writer whitelist enforcement and metrics; introduces Writer commit reentrancy guard; treats certain Git errors as dangling refs returning null; adds CI npm-audit step and SECURITY.md; bumps version to 11.0.0; updates docs, types, and tests to reflect these changes.

Changes

Cohort / File(s) Summary
Package & Release Docs
package.json, CHANGELOG.md, ROADMAP.md, README.md
Bump version to 11.0.0; document breaking default autoMaterialize=true and roadmap/migration notes; update README/examples.
CI & Security Docs
.github/workflows/ci.yml, SECURITY.md
Add "Security audit (runtime deps)" npm audit step (continue-on-error) and add/expand SECURITY.md (threat model, dependency risk, writer auth examples).
User Guide & Examples
docs/GUIDE.md, examples/edge-properties.js
Update guide and examples for new default autoMaterialize and provide migration/opt-out guidance; remove explicit autoMaterialize where appropriate.
Core: WarpGraph materialization
src/domain/WarpGraph.js, src/domain/errors/QueryError.js, index.d.ts
Flip default autoMaterialize false→true in open/constructor; centralize and update materialization error messages; expose autoMaterialize?: boolean in types and update JSDoc.
HTTP Sync & Auth
src/domain/services/HttpSyncServer.js, src/domain/services/SyncAuthService.js, index.d.ts
Add allowedWriters option wired into auth init; require auth.keys when whitelist set; add writer verification/enforcement APIs and forbiddenWriterRejects metric; update docs/typings.
Writer reentrancy guard
src/domain/warp/Writer.js
Add per-writer _commitInProgress flag and non-reentrancy guard in commitPatch; throw COMMIT_IN_PROGRESS on nested commits and ensure flag reset in finally.
Git adapter dangling-ref handling
src/infrastructure/adapters/GitGraphAdapter.js
Add isDanglingObjectError(err) helper; treat some git exit-code 128/stderr patterns as dangling/missing object so refExists returns false and readRef returns null instead of throwing.
Integration Tests
test/integration/WarpGraph.integration.test.js
Add CAS/patch integration tests covering patch advancement, sequential patches, reentrancy behavior, and failure handling.
Unit Tests — WarpGraph materialization
test/unit/domain/WarpGraph.*.test.js (multiple files)
Update many unit tests to expect autoMaterialize default=true; add explicit autoMaterialize: false where needed; update error-message assertions to reference "No materialized state" and docs URL.
Unit Tests — Writer/Auth/Git adapter/HTTP
test/unit/domain/warp/Writer.test.js, test/unit/domain/services/SyncAuthService.test.js, test/unit/domain/services/GitGraphAdapter.test.js, test/unit/domain/services/HttpSyncServer*.test.js
Add tests for commitPatch reentrancy guard, SyncAuthService.verifyWriters/enforce behavior and metrics, Git adapter dangling-ref handling, and HttpSyncServer allowedWriters/auth interactions.
Types
index.d.ts
Add autoMaterialize?: boolean to WarpGraph.open options and allowedWriters?: string[] to server/auth options; document non-reentrancy on Writer.commitPatch.

Sequence Diagrams

sequenceDiagram
    participant Client
    participant HttpSyncServer
    participant SyncAuthService
    participant Graph
    Client->>HttpSyncServer: POST /sync (patches, writerId, signature)
    HttpSyncServer->>HttpSyncServer: parse body
    HttpSyncServer->>SyncAuthService: verify/auth signature
    SyncAuthService-->>HttpSyncServer: { ok: true } / { ok:false }
    alt allowedWriters configured
        HttpSyncServer->>SyncAuthService: verifyWriters(writerId)
        SyncAuthService-->>HttpSyncServer: { ok:true } or { ok:false, FORBIDDEN_WRITER }
        alt ok
            HttpSyncServer->>Graph: graph.patch / Writer.commitPatch
            Graph-->>HttpSyncServer: result
            HttpSyncServer-->>Client: 200 OK
        else forbidden
            HttpSyncServer-->>Client: 403 Forbidden
        end
    else no whitelist (or log-only)
        HttpSyncServer->>Graph: graph.patch / Writer.commitPatch
        Graph-->>HttpSyncServer: result
        HttpSyncServer-->>Client: 200 OK
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped into code with a curious twitch,
Auto-materialize now lights up the switch.
Writers checked at the gate, forbidden ones logged,
Dangling refs softened, commits guarded and bogged,
Tests hop and thump — the graph sleeps enriched.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'v11.0.0: Hardening Sprint' accurately and concisely reflects the main objective of this PR: a major version release with hardening improvements including security features, breaking changes, and resilience enhancements.
Docstring Coverage ✅ Passed Docstring coverage is 90.00% which is sufficient. The required threshold is 80.00%.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hardening-sprint

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

Release Preflight

  • package version: 11.0.0
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v11.0.0, release workflow will publish.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ROADMAP.md (1)

439-449: ⚠️ Potential issue | 🟡 Minor

B1 not marked DONE despite writer whitelist being implemented in this PR.

The PR objectives list "B1: Add writer whitelist for sync ingress" as a deliverable, and the allowedWriters feature is implemented in SyncAuthService and HttpSyncServer. However, B1 on line 439 is not marked ~~DONE~~ like B8–B10. If B1 is only partially fulfilled (e.g., "enforced signed commits" is still pending), consider noting that in the description; otherwise, mark it done for consistency.

🤖 Fix all issues with AI agents
In `@docs/GUIDE.md`:
- Around line 1749-1773: Update the GUIDE text to use the correct release
version and fix the async example calls: change the release mention "As of
v10.9.0" to "As of v11.0.0" (to match the PR bump) and update the opt-out note
to reference the prior behavior version consistently (e.g., "preserve v10.9.0
behavior" or adjust both to v11.0.0/v10.9.0 as appropriate for release notes),
and add missing await keywords to the async getNodes() calls in the examples
(both the pre-change example that calls await graph.materialize(); const nodes =
await graph.getNodes(); and the post-change example const nodes = await
graph.getNodes();), referencing WarpGraph.open, materialize(), and getNodes()
where changes are needed.

In `@SECURITY.md`:
- Around line 131-132: The SECURITY.md risk table currently lists `strip-ansi`
and `open` though they are not direct dependencies; update the table to either
remove these two rows or annotate them as transitive/inlined (e.g., note that
`strip-ansi` was inlined into src/visualization/utils/ansi.js and `open` only
appears transitively in the lockfile), and ensure any mention references the
actual artifact names (`strip-ansi`, `open`) and the inlined location
(`src/visualization/utils/ansi.js`) so auditors see they are not present in
package.json.

In `@src/domain/services/HttpSyncServer.js`:
- Around line 240-243: The constructor currently accepts allowedWriters but
initAuth returns auth:null when auth.keys is missing, so allowedWriters is
silently ignored; update the constructor in HttpSyncServer to validate that if
allowedWriters is provided but auth.keys is not configured (check the auth
parameter or result of initAuth), throw a clear configuration error instead of
silently storing this._allowedWriters, and ensure callers of initAuth/authMode
and checkWriterWhitelist (and the SyncAuthService integration) cannot bypass
enforcement; reference initAuth, this._allowedWriters, auth.keys,
checkWriterWhitelist and SyncAuthService when making the change.
🧹 Nitpick comments (5)
test/unit/domain/services/GitGraphAdapter.test.js (2)

543-543: Misleading describe name: tests exercise readRef, not refExists directly.

The suite is named refExists() dangling-ref handling but all assertions call adapter.readRef(...). Consider renaming to readRef() dangling-ref handling to match what's actually being tested.


558-584: Tests only cover the refExists code path within readRef, not the rev-parse fallback.

Since mockPlumbing.execute is set to reject with the same error for all calls, refExists (the show-ref call) catches the dangling error and returns false, so readRef returns null at line 539–540 without ever reaching the rev-parse + isDanglingObjectError catch at line 551.

To also cover the TOCTOU race path (ref exists during show-ref but becomes dangling before rev-parse), consider a test where the first execute call (show-ref) resolves successfully, and the second (rev-parse) rejects with a dangling error.

Example test for the rev-parse dangling path
+    it('readRef returns null when show-ref succeeds but rev-parse hits dangling object', async () => {
+      // show-ref succeeds (ref exists)
+      mockPlumbing.execute
+        .mockResolvedValueOnce('') // show-ref --verify --quiet
+        .mockRejectedValueOnce((() => {
+          const err = /** `@type` {any} */ (new Error('fatal: bad object'));
+          err.details = { code: 128, stderr: 'fatal: bad object abc123' };
+          return err;
+        })());
+
+      const result = await adapter.readRef('refs/warp/test/writers/alice');
+      expect(result).toBeNull();
+    });
src/domain/warp/Writer.js (1)

179-194: Solid reentrancy guard with proper finally cleanup.

The try/finally ensures the flag resets on both success and error paths. The synchronous flag check before any await correctly prevents both nested and concurrent commitPatch calls on the same Writer instance.

One minor note: the @throws JSDoc (line 170) doesn't list the new COMMIT_IN_PROGRESS error code.

Add COMMIT_IN_PROGRESS to the `@throws` docs
    * `@throws` {WriterError} EMPTY_PATCH if no operations were added
    * `@throws` {WriterError} WRITER_REF_ADVANCED if CAS fails (ref moved since beginPatch)
    * `@throws` {WriterError} PERSIST_WRITE_FAILED if git operations fail
+   * `@throws` {WriterError} COMMIT_IN_PROGRESS if called while another commitPatch is in progress
src/domain/services/SyncAuthService.js (1)

385-395: Forbidden writer rejections are not logged.

Unlike the verify() method which routes failures through this._fail() (incrementing authFailCount and logging via this._logger.warn), verifyWriters() only increments the forbiddenWriterRejects metric without logging. Consider logging the forbidden writer IDs at warn level so operators can identify misconfigured or malicious sync peers.

♻️ Suggested logging addition
   verifyWriters(writerIds) {
     if (!this._allowedWriters) {
       return { ok: true };
     }
     const forbidden = writerIds.filter(id => !/** `@type` {Set<string>} */ (this._allowedWriters).has(id));
     if (forbidden.length > 0) {
       this._metrics.forbiddenWriterRejects += 1;
+      this._logger.warn('sync auth: forbidden writers rejected', { forbidden });
       return fail('FORBIDDEN_WRITER', 403);
     }
     return { ok: true };
   }
src/domain/WarpGraph.js (1)

1076-1081: Consider extracting the duplicated E_NO_STATE message into a constant.

The same long error string (with URL) is repeated verbatim in join(), runGC(), applySyncResponse(), and _ensureFreshState(). A shared constant would reduce drift risk if the URL or wording changes.

Example
+const E_NO_STATE_MSG = 'No materialized state. Call materialize() before querying, or use autoMaterialize: true (the default). See https://github.com/git-stunts/git-warp#materialization';
+
 // Then in each throw site:
-throw new QueryError('No materialized state. Call materialize() before querying, or use autoMaterialize: true (the default). See https://github.com/git-stunts/git-warp#materialization', {
+throw new QueryError(E_NO_STATE_MSG, {
   code: 'E_NO_STATE',
 });

Also applies to: 1737-1742, 2221-2226

Global git config was accidentally set to CI Bot <ci@git-cms.local>.
Restored to James Ross <james@flyingrobots.dev> to match GPG signing key.
Prior commits on this branch are correctly signed but show as unverified
on GitHub due to the author mismatch.
@github-actions
Copy link

Release Preflight

  • package version: 11.0.0
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v11.0.0, release workflow will publish.

- README.md: query section no longer says to pass autoMaterialize: true
- index.d.ts: document autoMaterialize default as true, add
  commitPatch() reentrancy @throws, add allowedWriters to
  SyncAuthServerOptions and serve() types
- Writer.js: add missing @throws COMMIT_IN_PROGRESS to commitPatch()
- GitGraphAdapter.js: readRef() JSDoc notes dangling-object handling
- examples/edge-properties.js: remove redundant autoMaterialize: true
@github-actions
Copy link

Release Preflight

  • package version: 11.0.0
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v11.0.0, release workflow will publish.

Previously, passing allowedWriters without configuring auth.keys would
silently ignore the whitelist. Now throws a clear configuration error
at construction time.
- GUIDE.md migration section: v10.9.0 → v11.0.0, add missing await on
  getNodes(), "preserve v10.8 behavior" → "preserve pre-v11 behavior"
- SECURITY.md: annotate strip-ansi as inlined and open as transitive
- Extract E_NO_STATE_MSG / E_STALE_STATE_MSG constants in WarpGraph.js
- Add warn logging for forbidden writer rejections in SyncAuthService
- Rename test describe "refExists()" → "readRef()" to match tested method
- Add TOCTOU race path test (show-ref ok, rev-parse dangling)
- Remove redundant autoMaterialize: true from README and example
- Annotate ROADMAP B1 as partially done (whitelist done, signed commits open)
@flyingrobots
Copy link
Member Author

@coderabbitai Re: Writer.js @throws nit — this was already addressed in commit 20757fc (the docs sweep) before your review. COMMIT_IN_PROGRESS was added to both src/domain/warp/Writer.js:170 and index.d.ts:1337.

@coderabbitai
Copy link

coderabbitai bot commented Feb 15, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check Writer.js around line 170 for COMMIT_IN_PROGRESS documentation
sed -n '160,180p' src/domain/warp/Writer.js

echo "---"

# Check index.d.ts around line 1337 for COMMIT_IN_PROGRESS documentation
sed -n '1330,1345p' index.d.ts

Length of output: 1326


@flyingrobots: You're absolutely right! I can confirm the @throws documentation for COMMIT_IN_PROGRESS is present in both locations:

  • src/domain/warp/Writer.js:170 includes the full JSDoc with the reentrancy guard error
  • index.d.ts:1342 has the corresponding TypeScript declaration comment

Thank you for pointing this out—the documentation was indeed already in place. ✓

@github-actions
Copy link

Release Preflight

  • package version: 11.0.0
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v11.0.0, release workflow will publish.

@github-actions
Copy link

Release Preflight

  • package version: 11.0.0
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v11.0.0, release workflow will publish.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/domain/services/SyncAuthService.js`:
- Around line 385-396: verifyWriters currently always returns a failure for
forbidden writer IDs; update it to respect the service auth mode like verify()
does by checking this._authMode (e.g. compare against 'log-only') before
returning fail. Specifically, in verifyWriters (and/or checkWriterWhitelist if
that helper exists) keep the metric increment and logger.warn for forbidden
writers, but if this._authMode === 'log-only' return { ok: true } instead of
fail('FORBIDDEN_WRITER', 403); only return the failure when auth mode enforces
blocking. Ensure you reference the verifyWriters function and this._authMode
property so behavior matches verify().

In `@src/domain/WarpGraph.js`:
- Around line 1741-1743: The throw in runGC() duplicates the hardcoded
E_NO_STATE message; replace the string literal with the shared constant
E_NO_STATE_MSG and pass the same error metadata (code: 'E_NO_STATE') so runGC()
throws new QueryError(E_NO_STATE_MSG, { code: 'E_NO_STATE' }) consistent with
other sites (see uses at lines referencing E_NO_STATE_MSG and the QueryError
constructor) — update the throw inside the runGC function to reference the
E_NO_STATE_MSG constant instead of the duplicated literal.
🧹 Nitpick comments (2)
src/domain/services/SyncAuthService.js (1)

176-183: Consider guarding against an empty allowedWriters array.

An empty array allowedWriters: [] will create an empty Set, silently rejecting all writers. This is likely a misconfiguration rather than intent. A validation here would save operators debugging time.

Proposed fix
     if (allowedWriters) {
+      if (allowedWriters.length === 0) {
+        throw new Error('allowedWriters must be a non-empty array when provided');
+      }
       for (const w of allowedWriters) {
         validateWriterId(w);
       }
test/unit/domain/services/HttpSyncServer.test.js (1)

308-325: Tests cover the config validation; consider adding a request-level whitelist enforcement test.

These two tests correctly verify the constructor guard. However, there's no unit test in this file for the runtime behavior — i.e., sending a sync request with a forbidden writer ID and asserting HTTP 403. The integration tests (B1/B9) may cover this, but a focused unit test here would improve confidence in checkWriterWhitelist.

Also a minor nit: lines 311 and 319 pass createMockPort() (the wrapper object) as httpPort, but the constructor expects the .port sub-object (as used in the beforeEach on line 128). This works only because the constructor throws or completes before touching httpPort. Using createMockPort().port would be more accurate.

- Replace hardcoded E_NO_STATE error string in runGC() with the shared
  E_NO_STATE_MSG constant (CodeRabbit review 2, comment #2808672021)
- Pass authMode to checkWriterWhitelist() so log-only mode correctly
  allows forbidden writers through instead of blocking them
- Extract _validateAllowedWriters() helper to reduce constructor complexity
- Validate empty allowedWriters array at SyncAuthService construction
- Add test coverage for allowedWriters + log-only mode interaction
  and empty allowedWriters rejection
@github-actions
Copy link

Release Preflight

  • package version: 11.0.0
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v11.0.0, release workflow will publish.

- Convert standalone checkWriterWhitelist() to HttpSyncServer._checkWriterWhitelist()
  for consistency with _checkAuth() — both are instance methods that read
  this._auth and this._authMode, no parameter threading needed.
- Add JSDoc on SyncAuthService.verifyWriters() clarifying that mode
  enforcement is the caller's responsibility (matching verify() pattern).
- Add mode-agnostic documentation-by-test: explicitly verify that both
  verify() and verifyWriters() return { ok: false } regardless of mode,
  proving mode enforcement lives in HttpSyncServer, not SyncAuthService.
@github-actions
Copy link

Release Preflight

  • package version: 11.0.0
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v11.0.0, release workflow will publish.

- Combine _checkAuth() and _checkWriterWhitelist() into a single
  _authorize(request, parsed) method on HttpSyncServer. Both checks
  share the same enforce/log-only pattern against this._auth and
  this._authMode — no reason to split them across two methods.
- Simplify _handleRequest: body parsing now precedes auth (body size
  DoS guard remains first), reducing the pipeline from 6 steps to 5.
- Add SyncAuthService.enforceWriters() — mode-aware convenience wrapper
  around verifyWriters(). Returns { ok: true } in log-only mode after
  recording metrics, eliminating the need for callers to check mode.
- Add end-to-end log-only integration test: bad signature + forbidden
  writer both pass through, sync succeeds, logger.warn called.
@github-actions
Copy link

Release Preflight

  • package version: 11.0.0
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v11.0.0, release workflow will publish.

@flyingrobots
Copy link
Member Author

@coderabbitai review please

@coderabbitai
Copy link

coderabbitai bot commented Feb 15, 2026

@flyingrobots: I'll review the changes in this PR now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

- Replace manual verifyWriters() + mode check in _authorize() with
  enforceWriters(), which handles log-only passthrough internally.
- Strengthen log-only e2e test: assert logger.warn called >= 2 times
  (once for auth failure, once for forbidden writer) confirming both
  failures are independently logged.
@github-actions
Copy link

Release Preflight

  • package version: 11.0.0
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v11.0.0, release workflow will publish.

@flyingrobots flyingrobots merged commit 59339bf into main Feb 15, 2026
7 checks passed
@flyingrobots flyingrobots deleted the hardening-sprint branch February 15, 2026 08:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant