Skip to content

release(0.3.2): repair the publish path end to end and lead the README with the product - #3055

Merged
oscharko merged 14 commits into
devfrom
claude/verify-artifacts-by-id
Aug 10, 2026
Merged

release(0.3.2): repair the publish path end to end and lead the README with the product#3055
oscharko merged 14 commits into
devfrom
claude/verify-artifacts-by-id

Conversation

@oscharko

@oscharko oscharko commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Summary

0.3.1 reached its GitHub release and npm under next, but its latest promotion was blocked by a defect in the publish verification itself — and the gates correctly bind every publish to the exact tagged code, so the repair ships as 0.3.2 with the identical product. This PR carries three things:

1. The publish path, repaired end to end (every 0.3.1 outage, fixed at the owning layer)

  • Digest verification restricts its collision refusal to the four published download names; per-target evidence files sharing a name across staging artifacts (SHA256SUMS.txt, sbom.cdx.json, …) no longer refuse a healthy release.
  • Evaluation artifacts are fetched by their immutable ids through a binary-safe port (in-process fail-closed ZIP32 reader added to zip-archive.mjs); a rerun of the build run can neither substitute bytes nor block a verified release. The mutable run listing is no longer consulted — pinned by a test that throws on any listing call.
  • --provenance is passed exactly where an OIDC provider exists; token publishes carry no attestation instead of dying on the flag.
  • The publish states strict-ssl=true in its own npmrc and environment; a user-level strict-ssl=false neither weakens nor blocks a release.
  • The release-owner allowlist resolves through one shared helper (scripts/lib/release-owner-allowlist.mjs) in both the producer and the publisher.
  • release.yml pins the governed npm from EXPECTED_PACKAGE_MANAGER, held in lockstep by release-workflow-npm-pin.test.mjs — the drifted hand pin (11.18.0 vs 11.16.0) killed the CI publish from the frozen 0.3.1 tag.
  • The release-branch lookup treats only an explicit (HTTP 404) as absent and travels URL-encoded; --public-release requires an entirely successful build run; an auth preflight refuses a live publish with neither token nor OIDC before prepack.

2. The README, led by the product — one page: what Keiko is, the desktop downloads with first-launch steps, a two-command npm quickstart, what the 0.3 line ships, the principles. Developer content lives in CONTRIBUTING.md and docs/.

3. The 0.3.2 release cut — versions across all workspaces and constants, lockfile, and the reviewed release-impact entry with the owner approval (github-issue-comment:oscharko-dev/Keiko#2802#5235616947, verified live).

Reuse / No-Duplication

The ZIP reader extends the existing zip-archive.mjs (same constants, same traversal rule, round-trip-tested against its writer). Allowlist resolution is ONE shared helper consumed by both release scripts. No new subsystem.

Verification

Run locally on this branch:

  • All touched suites green: release-publish-pipeline 71, portable-release-verification 76, release-portable-prerelease 69, portable-evaluation-manifest 18, zip-archive 14, release-workflow-npm-pin 1, zizmor-anchors 12 — 318 total in the battery run
  • npm run typecheck PASS, npm run lint clean, npm run format:check clean
  • npm run check:version-consistency PASS (0.3.2 everywhere), npm run check:release-impact PASS and the publish-mode check PASS with the approval verified live
  • npm run gates:sonar PASS

Update-Impact

Release metadata and release tooling; no product runtime change relative to 0.3.1. Recorded in the catalog (2026-08-10-keiko-0.3.2-public-download): no state action required.

🤖 Generated with Claude Code

@oscharko
oscharko temporarily deployed to keiko-for-quality August 9, 2026 20:18 — with GitHub Actions Inactive
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@oscharko, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 26 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ed7bbfe6-9e47-4cdf-bd8c-5659dae742e0

📥 Commits

Reviewing files that changed from the base of the PR and between 78fb97b and 72ea8ea.

📒 Files selected for processing (10)
  • docs/adr/ADR-0130-npm-trusted-publishing-for-release-pipeline.md
  • release-impact.catalog.json
  • scripts/__tests__/check-zizmor-anchors.test.mjs
  • scripts/__tests__/portable-evaluation-manifest.test.mjs
  • scripts/__tests__/portable-release-verification.test.mjs
  • scripts/__tests__/zip-archive.test.mjs
  • scripts/check-zizmor-anchors.mjs
  • scripts/lib/portable-evaluation-manifest.mjs
  • scripts/lib/zip-archive.mjs
  • scripts/release-publish.mjs
📝 Walkthrough

Walkthrough

Changes

The release pipeline now validates immutable ZIP artifacts, applies signing-scope gates, strengthens evaluation and stable publication checks, and supports conditional npm provenance. Repository packages, exported version constants, tests, documentation, and release metadata are aligned with version 0.3.2.

Release pipeline

Layer / File(s) Summary
Release scope and contracts
.github/workflows/portable-assets.yml, release-impact.catalog.json, docs/adr/*, README.md
Release metadata and workflow gates now distinguish evaluation and production signing scopes. The README and release contracts describe the 0.3 release.
ZIP and artifact verification
scripts/lib/zip-archive.mjs, scripts/lib/portable-release-verification.mjs, scripts/__tests__/*
Artifact verification uses immutable records and ZIP extraction with path, size, CRC, and digest validation.
Evaluation release safeguards
scripts/release-portable-prerelease.mjs, scripts/lib/release-owner-allowlist.mjs
Public releases require successful, attempt-consistent workflow evidence and encoded branch lookups.
Stable publication orchestration
scripts/release-publish.mjs, scripts/__tests__/release-publish-pipeline.test.mjs
Stable publication verifies portable assets, enforces TLS, validates authentication, and requests provenance only with OIDC credentials.
Governed npm pin
.github/workflows/release.yml, scripts/check-runtime-toolchain.mjs, scripts/__tests__/release-workflow-npm-pin.test.mjs
The release workflow and regression test use the exported npm@11.16.0 toolchain constant.

Package version alignment

Layer / File(s) Summary
Package manifests and exported versions
package.json, packages/*/package.json, packages/*/src/version.ts, packages/keiko-contracts/src/index.ts
Package versions, internal dependency pins, and exported version constants are updated from 0.3.0 to 0.3.2.
Manifest-backed version assertions
packages/*/src/*.test.ts
Version tests read package.json and compare exported constants with the packaged manifest version.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: 🕐 40+ Minutes, dependencies, javascript

🚥 Pre-merge checks | ✅ 1 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers summary, reuse, verification, and release impact, but omits Scope, Delivery Board, Product Impact, Review And Closure, Risk Notes, and an issue reference. Add the missing template sections, issue reference, required checkbox states, risk notes, and closure evidence.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title uses an imperative conventional subject and names the repaired publish path and product-focused README outcome.

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.

Comment thread scripts/__tests__/build-runtime-supervisor.test.mjs
Comment thread scripts/lib/portable-evaluation-manifest.mjs
Comment thread scripts/__tests__/release-publish-pipeline.test.mjs Outdated
Comment thread scripts/__tests__/release-publish-pipeline.test.mjs
Comment thread scripts/lib/portable-release-verification.mjs Outdated
@keiko-for-quality

keiko-for-quality Bot commented Aug 9, 2026

Copy link
Copy Markdown

Keiko for Quality — run summary

COMPLETE · head 72ea8ea · 2026-08-10T05:33:36Z · engine v1.8.4 · action 0c53cb11a924ba62dc2172316dee4ecf4a758360

Metric Count
Total paths 79
Reviewable 77
Excluded 2
Mechanically clean 0
Critical pointer changes (content not reviewable) 0
Replayed from cache 75
Cache miss (path-set shape changed) 0
Freshly reviewed 2
Findings published 1
Suppressed (intra-run duplicate) 0
Suppressed (exact duplicate) 0
Suppressed (similar) 0
Suppressed (dispositioned) 4
Suppressed (outdated recurrence) 0
Rejected (sanitization) 0
Rejected (placement) 0
Read-back failures 0
API failures 0
Duration (s) 33
Tokens per published finding 53220

Budget: 550030 tokens allotted, 53220 reported

Recent runs

  • 72ea8ea · complete · fresh 2 · replayed 75 · 33s
  • 2e9feb2 · complete · fresh 11 · replayed 66 · 63s
  • 15208c2 · complete · fresh 20 · replayed 57 · 84s
  • 78fb97b · complete · fresh 2 · replayed 71 · 10s
  • c022234 · complete · fresh 4 · replayed 68 · 26s

@oscharko
oscharko temporarily deployed to keiko-for-quality August 9, 2026 20:20 — with GitHub Actions Inactive

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 48b421d41b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/release-portable-prerelease.mjs Outdated
Comment thread scripts/release-publish.mjs Outdated
@oscharko oscharko changed the title fix(release): fetch evaluation artifacts by immutable id and align the release gates release(0.3.2): repair the publish path end to end and lead the README with the product Aug 10, 2026
@oscharko
oscharko temporarily deployed to keiko-for-quality August 10, 2026 03:44 — with GitHub Actions Inactive
Comment thread packages/keiko-contracts/package.json
Comment thread packages/keiko-contracts/src/index.test.ts Outdated
Comment thread packages/keiko-evidence/package.json
Comment thread packages/keiko-evidence/src/index.test.ts Outdated
Comment thread packages/keiko-harness/package.json
Comment thread packages/keiko-harness/src/index.test.ts Outdated
Comment thread packages/keiko-memory-consolidation/src/version.ts
Comment thread packages/keiko-memory-retrieval/src/version.ts
Comment thread packages/keiko-model-gateway/src/index.test.ts Outdated
Comment thread packages/keiko-model-gateway/src/version.ts
Comment thread packages/keiko-model-gateway/package.json
Comment thread packages/keiko-security/src/version.ts
Comment thread packages/keiko-tools/package.json
Comment thread packages/keiko-tools/src/version.ts
Comment thread scripts/__tests__/build-runtime-supervisor.test.mjs
Comment thread scripts/lib/portable-release-verification.mjs
…tate doc

The staged-manifest test pins the reviewed portable contract object,
which now declares signingScope, and the local runtime state contract
introduces itself at 0.3.2 with 0.3.1 as the unchanged prior state
(Codex findings on #3055).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@oscharko
oscharko temporarily deployed to keiko-for-quality August 10, 2026 04:37 — with GitHub Actions Inactive
@oscharko
oscharko temporarily deployed to keiko-for-quality August 10, 2026 04:38 — with GitHub Actions Inactive
The security posture pin asserted the old refusal message; enforcement
moved to script-owned transport policy in this PR (strict-ssl=true in
the publish's own userconfig AND environment — strictly stronger), and
provenance attestation is pinned to the dual-value OIDC predicate.
Relocated and strengthened, never relaxed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@oscharko
oscharko temporarily deployed to keiko-for-quality August 10, 2026 05:00 — with GitHub Actions Inactive
@oscharko
oscharko temporarily deployed to keiko-for-quality August 10, 2026 05:00 — with GitHub Actions Inactive

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 10

♻️ Duplicate comments (1)
scripts/release-portable-prerelease.mjs (1)

1219-1239: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Build the evaluation manifest before mutating the release.

evaluationManifestFor() can fail when artifact identities are invalid. It currently runs after ensureTagRefAtBuiltCommit() creates the tag and after deleteInterruptedDraft() deletes a resumable draft. A failed manifest leaves repository state that requires manual recovery.

Resolve the manifest after the plan-only return and before Lines 1219-1220. Then pass the saved path to createRelease().

Proposed fix
   if (options.planOnly) {
     reportPlan(body, tag, hasPendingDraft);
     return;
   }
+  const evaluationManifestPath = evaluationManifestFor({
+    options,
+    workDir,
+    tag,
+    repository,
+    runId,
+    attempt: view.attempt,
+    digests,
+    sourceCommitSha: view.headSha,
+  });
   ensureTagRefAtBuiltCommit(tag, view.headSha);
   if (hasPendingDraft) deleteInterruptedDraft(tag);
   createRelease({
@@
-    evaluationManifestPath: evaluationManifestFor({
-      options,
-      workDir,
-      tag,
-      repository,
-      runId,
-      attempt: view.attempt,
-      digests,
-      sourceCommitSha: view.headSha,
-    }),
+    evaluationManifestPath,
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/release-portable-prerelease.mjs` around lines 1219 - 1239, Move the
evaluationManifestFor call out of the createRelease arguments and execute it
after the plan-only return but before ensureTagRefAtBuiltCommit and
deleteInterruptedDraft. Store its resulting path, then pass that saved path to
createRelease via evaluationManifestPath so manifest failures occur before
release state is mutated.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/zizmor.yml:
- Around line 93-95: Update .github/zizmor.yml lines 93-95 so
portable-assets.yml:166 and portable-assets.yml:236 target the MSVC steps
invoking vswhere.exe and vcvars64.bat, not the 7z verification step. Update
.github/zizmor.yml lines 96-101 so release.yml:145 targets the npm@11.16.0
installation step. Extend npm run check:zizmor-anchors to validate both
portable-assets anchors and the release.yml anchor against their intended
commands and version pin.

In `@docs/adr/ADR-0130-npm-trusted-publishing-for-release-pipeline.md`:
- Around line 66-76: Update ADR-0130’s D2 and Consequences sections to replace
all stale npm@10.9.8 and 10.9.x references with the governed npm@11.16.0 pin,
and remove wording that describes the publish npm as newer than the repository
npm. Add a Version History entry documenting this pin update, following the
ADR’s existing Revision Policy and table format.

In `@release-impact.catalog.json`:
- Line 1028: Update the userVisibleSummary text to describe the four published
artifacts as “four published downloads” instead of “four published archives,”
preserving the surrounding release description unchanged.

In `@scripts/__tests__/portable-release-verification.test.mjs`:
- Around line 517-521: Update the mock’s collectRunArtifactDigests function to
accept the production four-argument signature, including relevantAssetNames, and
record that fourth argument under the names key while preserving artifacts
separately. Update the related assertions to verify the forwarded relevant asset
names, ensuring the test exercises portableReleaseGate’s filtering contract.

In `@scripts/__tests__/registry-install-smoke-security.test.mjs`:
- Around line 110-113: Replace the substring assertions in the provenance test
with a reference to the established behavioral coverage in
release-publish-pipeline.test.mjs, which verifies both conditional branches.
Remove the direct --provenance and ACTIONS_ID_TOKEN_REQUEST_TOKEN source checks
while preserving coverage through those existing tests.

In `@scripts/__tests__/zip-archive.test.mjs`:
- Around line 332-357: Extend the extractZipArchiveEntries tests with two
malformed-archive cases using the existing reader fixtures: mutate a byte in the
compressed stream and assert the declared size or checksum error, then truncate
the archive immediately after its local header and assert the truncated error.
Keep the tests focused on the extractor’s extractEntryData and readAt refusal
paths, alongside the existing healthy and hostile-entry cases.

In `@scripts/lib/portable-release-verification.mjs`:
- Around line 520-527: Validate manifest-declared artifact fields inside the
exported collection flow before use: require each declared name to be a safe
artifact name that cannot escape root via join, and require each declared id to
be numeric before constructing the GitHub API request in fetchArtifactZip or its
caller. Reject invalid declarations by returning the existing empty Map failure
result, rather than relying on runArtifactDigestFailures’ caller-side
validation.

In `@scripts/lib/zip-archive.mjs`:
- Around line 344-348: Update inflatedEntryData to pass the declared entry.size
as maxOutputLength when calling inflateRawSync, ensuring decompression is
bounded before centralEntryData and extractEntryData compare the resulting
length. Preserve the existing handling for STORE_METHOD and unsupported
compression methods.
- Around line 419-428: Update both readZipArchiveEntries() and
extractZipArchiveEntries() to reject ZIP64 EOCD sentinel values before using the
parsed fields: refuse a count of 0xffff or a directory offset of 0xffffffff with
the established malformed/unsupported ZIP error behavior. Apply the validation
immediately after locating the EOCD and before iterating entries or reading the
central directory.

In `@scripts/release-publish.mjs`:
- Around line 250-265: Update fetchRunArtifactZip to enforce ADR-0121’s transfer
deadline and maximum ZIP size while fetching the artifact, rather than relying
on unbounded spawnSync output. Treat timeout, oversize, and other refusal
conditions as failures, remove the partial destination file before returning,
and preserve cleanup of the file descriptor; add negative tests covering timeout
and size-limit failures.

---

Duplicate comments:
In `@scripts/release-portable-prerelease.mjs`:
- Around line 1219-1239: Move the evaluationManifestFor call out of the
createRelease arguments and execute it after the plan-only return but before
ensureTagRefAtBuiltCommit and deleteInterruptedDraft. Store its resulting path,
then pass that saved path to createRelease via evaluationManifestPath so
manifest failures occur before release state is mutated.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0bf37ac2-fece-46a3-8c63-f57917bc6fb0

📥 Commits

Reviewing files that changed from the base of the PR and between 8786a0d and 78fb97b.

📒 Files selected for processing (30)
  • .github/workflows/portable-assets.yml
  • .github/zizmor.yml
  • docs/adr/ADR-0121-portable-managed-install-and-release-asset-update-authority.md
  • docs/adr/ADR-0130-npm-trusted-publishing-for-release-pipeline.md
  • docs/local-runtime-state-contract.md
  • packages/keiko-contracts/src/index.test.ts
  • packages/keiko-evidence/src/index.test.ts
  • packages/keiko-harness/src/index.test.ts
  • packages/keiko-memory-consolidation/src/index.test.ts
  • packages/keiko-memory-consolidation/src/version.test.ts
  • packages/keiko-memory-governance/src/index.test.ts
  • packages/keiko-memory-governance/src/version.test.ts
  • packages/keiko-memory-retrieval/src/index.test.ts
  • packages/keiko-memory-retrieval/src/version.test.ts
  • packages/keiko-model-gateway/src/index.test.ts
  • packages/keiko-security/src/index.test.ts
  • packages/keiko-tools/src/index.test.ts
  • packages/keiko-tools/src/version.test.ts
  • packages/keiko-workspace/src/index.test.ts
  • release-impact.catalog.json
  • scripts/__tests__/portable-release-verification.test.mjs
  • scripts/__tests__/portable-runtime.test.mjs
  • scripts/__tests__/registry-install-smoke-security.test.mjs
  • scripts/__tests__/release-portable-prerelease.test.mjs
  • scripts/__tests__/release-publish-pipeline.test.mjs
  • scripts/__tests__/zip-archive.test.mjs
  • scripts/lib/portable-release-verification.mjs
  • scripts/lib/zip-archive.mjs
  • scripts/release-portable-prerelease.mjs
  • scripts/release-publish.mjs
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (14)
**/*.{ts,tsx,mjs,js,json,md,yml,yaml}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer working, clean, secure, verified code over contradictory ADR text; when repairing behavior, update affected ADR sections in the same change and do not create a replacement ADR solely to repair outdated text.

Files:

  • docs/adr/ADR-0130-npm-trusted-publishing-for-release-pipeline.md
  • packages/keiko-memory-retrieval/src/version.test.ts
  • packages/keiko-memory-retrieval/src/index.test.ts
  • scripts/__tests__/portable-runtime.test.mjs
  • packages/keiko-tools/src/version.test.ts
  • packages/keiko-security/src/index.test.ts
  • packages/keiko-memory-governance/src/index.test.ts
  • scripts/__tests__/registry-install-smoke-security.test.mjs
  • packages/keiko-memory-consolidation/src/version.test.ts
  • packages/keiko-contracts/src/index.test.ts
  • packages/keiko-workspace/src/index.test.ts
  • packages/keiko-model-gateway/src/index.test.ts
  • packages/keiko-tools/src/index.test.ts
  • packages/keiko-memory-governance/src/version.test.ts
  • packages/keiko-harness/src/index.test.ts
  • scripts/__tests__/zip-archive.test.mjs
  • packages/keiko-evidence/src/index.test.ts
  • packages/keiko-memory-consolidation/src/index.test.ts
  • docs/local-runtime-state-contract.md
  • scripts/__tests__/release-portable-prerelease.test.mjs
  • docs/adr/ADR-0121-portable-managed-install-and-release-asset-update-authority.md
  • scripts/release-publish.mjs
  • scripts/lib/portable-release-verification.mjs
  • release-impact.catalog.json
  • scripts/lib/zip-archive.mjs
  • scripts/release-portable-prerelease.mjs
  • scripts/__tests__/portable-release-verification.test.mjs
  • scripts/__tests__/release-publish-pipeline.test.mjs
**/*

📄 CodeRabbit inference engine (AGENTS.md)

**/*: Preserve the human-control invariant: never weaken authority validation, trust boundaries, evidence redaction, governance gates, or fail-closed behavior.
Match naming, error handling, evidence redaction, and test style used by neighboring files in the same package.
Delete dead code rather than commenting it out; do not leave scaffolding or TODOs hiding unfinished branches.
Use English in code, comments, identifiers, documentation, commit messages, issues, and pull requests.
Inspect and reuse existing helpers, contracts, and subsystems before building; do not introduce parallel workspace, graph, policy, evidence, memory, connector, workflow, or UI subsystems without a documented capability gap.
Work on a non-dev branch named type/short-slug; never work directly on dev.
Use signed, imperative conventional commit subjects with a PR or issue reference, in English and without secrets.

Files:

  • docs/adr/ADR-0130-npm-trusted-publishing-for-release-pipeline.md
  • packages/keiko-memory-retrieval/src/version.test.ts
  • packages/keiko-memory-retrieval/src/index.test.ts
  • scripts/__tests__/portable-runtime.test.mjs
  • packages/keiko-tools/src/version.test.ts
  • packages/keiko-security/src/index.test.ts
  • packages/keiko-memory-governance/src/index.test.ts
  • scripts/__tests__/registry-install-smoke-security.test.mjs
  • packages/keiko-memory-consolidation/src/version.test.ts
  • packages/keiko-contracts/src/index.test.ts
  • packages/keiko-workspace/src/index.test.ts
  • packages/keiko-model-gateway/src/index.test.ts
  • packages/keiko-tools/src/index.test.ts
  • packages/keiko-memory-governance/src/version.test.ts
  • packages/keiko-harness/src/index.test.ts
  • scripts/__tests__/zip-archive.test.mjs
  • packages/keiko-evidence/src/index.test.ts
  • packages/keiko-memory-consolidation/src/index.test.ts
  • docs/local-runtime-state-contract.md
  • scripts/__tests__/release-portable-prerelease.test.mjs
  • docs/adr/ADR-0121-portable-managed-install-and-release-asset-update-authority.md
  • scripts/release-publish.mjs
  • scripts/lib/portable-release-verification.mjs
  • release-impact.catalog.json
  • scripts/lib/zip-archive.mjs
  • scripts/release-portable-prerelease.mjs
  • scripts/__tests__/portable-release-verification.test.mjs
  • scripts/__tests__/release-publish-pipeline.test.mjs
**/*.{ts,tsx,mjs,js,json,yml,yaml,md}

📄 CodeRabbit inference engine (AGENTS.md)

Keep secrets out of code, logs, evidence, configuration, tests, manifests, and diagnostics; evidence must contain only redacted counts, hashes, statuses, scopes, or summaries, never raw content, keys, endpoints, or PII.

Files:

  • docs/adr/ADR-0130-npm-trusted-publishing-for-release-pipeline.md
  • packages/keiko-memory-retrieval/src/version.test.ts
  • packages/keiko-memory-retrieval/src/index.test.ts
  • scripts/__tests__/portable-runtime.test.mjs
  • packages/keiko-tools/src/version.test.ts
  • packages/keiko-security/src/index.test.ts
  • packages/keiko-memory-governance/src/index.test.ts
  • scripts/__tests__/registry-install-smoke-security.test.mjs
  • packages/keiko-memory-consolidation/src/version.test.ts
  • packages/keiko-contracts/src/index.test.ts
  • packages/keiko-workspace/src/index.test.ts
  • packages/keiko-model-gateway/src/index.test.ts
  • packages/keiko-tools/src/index.test.ts
  • packages/keiko-memory-governance/src/version.test.ts
  • packages/keiko-harness/src/index.test.ts
  • scripts/__tests__/zip-archive.test.mjs
  • packages/keiko-evidence/src/index.test.ts
  • packages/keiko-memory-consolidation/src/index.test.ts
  • docs/local-runtime-state-contract.md
  • scripts/__tests__/release-portable-prerelease.test.mjs
  • docs/adr/ADR-0121-portable-managed-install-and-release-asset-update-authority.md
  • scripts/release-publish.mjs
  • scripts/lib/portable-release-verification.mjs
  • release-impact.catalog.json
  • scripts/lib/zip-archive.mjs
  • scripts/release-portable-prerelease.mjs
  • scripts/__tests__/portable-release-verification.test.mjs
  • scripts/__tests__/release-publish-pipeline.test.mjs
docs/adr/ADR-*.md

📄 CodeRabbit inference engine (AGENTS.md)

Read relevant ADRs before changing boundaries, update affected sections when recorded behavior becomes outdated or unsafe, add genuinely new ADRs using the next free number, register them in the index, and never renumber existing ADRs.

Files:

  • docs/adr/ADR-0130-npm-trusted-publishing-for-release-pipeline.md
  • docs/adr/ADR-0121-portable-managed-install-and-release-asset-update-authority.md
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript strict settings, avoid any, provide explicit function return types, model states with discriminated unions, and narrow values instead of casting.
Keep cyclomatic complexity at most 10 and each function at most 50 non-comment, non-blank lines; extract helpers when necessary.
Do not use console.* for real product output; route output through the intended logger or diagnostic sink.
Fix bugs at the layer that owns the invariant and address the whole class of bug rather than patching individual call sites.
Do not silently swallow errors; avoid empty catches, provide diagnostic context, and include a correlation ID for server errors connecting opaque UI 500 responses to redacted operator diagnostics.
Validate hostile workspace input, model output, and connector data before processing; generated code must remain behind the sandbox egress boundary.
Keep tests hermetic: no real network, shared mutable global state, wall-clock or ordering races, or reliance on an available port; await conditions instead of sleeping and use deterministic self-contained fixtures.
Keep coverage from falling below the committed per-file baseline; add tests for new code and do not add competing Vitest thresholds or hard-coded coverage values.

Files:

  • packages/keiko-memory-retrieval/src/version.test.ts
  • packages/keiko-memory-retrieval/src/index.test.ts
  • packages/keiko-tools/src/version.test.ts
  • packages/keiko-security/src/index.test.ts
  • packages/keiko-memory-governance/src/index.test.ts
  • packages/keiko-memory-consolidation/src/version.test.ts
  • packages/keiko-contracts/src/index.test.ts
  • packages/keiko-workspace/src/index.test.ts
  • packages/keiko-model-gateway/src/index.test.ts
  • packages/keiko-tools/src/index.test.ts
  • packages/keiko-memory-governance/src/version.test.ts
  • packages/keiko-harness/src/index.test.ts
  • packages/keiko-evidence/src/index.test.ts
  • packages/keiko-memory-consolidation/src/index.test.ts
**/*.{ts,tsx,mjs,js}

📄 CodeRabbit inference engine (AGENTS.md)

Use Prettier formatting: 2-space indentation, double quotes, semicolons, 100-column print width, trailing commas, and LF endings.

Files:

  • packages/keiko-memory-retrieval/src/version.test.ts
  • packages/keiko-memory-retrieval/src/index.test.ts
  • scripts/__tests__/portable-runtime.test.mjs
  • packages/keiko-tools/src/version.test.ts
  • packages/keiko-security/src/index.test.ts
  • packages/keiko-memory-governance/src/index.test.ts
  • scripts/__tests__/registry-install-smoke-security.test.mjs
  • packages/keiko-memory-consolidation/src/version.test.ts
  • packages/keiko-contracts/src/index.test.ts
  • packages/keiko-workspace/src/index.test.ts
  • packages/keiko-model-gateway/src/index.test.ts
  • packages/keiko-tools/src/index.test.ts
  • packages/keiko-memory-governance/src/version.test.ts
  • packages/keiko-harness/src/index.test.ts
  • scripts/__tests__/zip-archive.test.mjs
  • packages/keiko-evidence/src/index.test.ts
  • packages/keiko-memory-consolidation/src/index.test.ts
  • scripts/__tests__/release-portable-prerelease.test.mjs
  • scripts/release-publish.mjs
  • scripts/lib/portable-release-verification.mjs
  • scripts/lib/zip-archive.mjs
  • scripts/release-portable-prerelease.mjs
  • scripts/__tests__/portable-release-verification.test.mjs
  • scripts/__tests__/release-publish-pipeline.test.mjs
packages/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Maintain package dependency direction: keiko-contracts is the leaf, security depends only on contracts, domain packages must not depend on the server, and the server must not depend on keiko-editor.

Files:

  • packages/keiko-memory-retrieval/src/version.test.ts
  • packages/keiko-memory-retrieval/src/index.test.ts
  • packages/keiko-tools/src/version.test.ts
  • packages/keiko-security/src/index.test.ts
  • packages/keiko-memory-governance/src/index.test.ts
  • packages/keiko-memory-consolidation/src/version.test.ts
  • packages/keiko-contracts/src/index.test.ts
  • packages/keiko-workspace/src/index.test.ts
  • packages/keiko-model-gateway/src/index.test.ts
  • packages/keiko-tools/src/index.test.ts
  • packages/keiko-memory-governance/src/version.test.ts
  • packages/keiko-harness/src/index.test.ts
  • packages/keiko-evidence/src/index.test.ts
  • packages/keiko-memory-consolidation/src/index.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.test.{ts,tsx}: Regression tests must fail before the fix and pass afterward; fixtures and mocks must call or derive from the production entry point rather than reimplementing its formula.
Test empty, boundary, malformed, and hostile inputs, including both branches of every guard introduced.

Files:

  • packages/keiko-memory-retrieval/src/version.test.ts
  • packages/keiko-memory-retrieval/src/index.test.ts
  • packages/keiko-tools/src/version.test.ts
  • packages/keiko-security/src/index.test.ts
  • packages/keiko-memory-governance/src/index.test.ts
  • packages/keiko-memory-consolidation/src/version.test.ts
  • packages/keiko-contracts/src/index.test.ts
  • packages/keiko-workspace/src/index.test.ts
  • packages/keiko-model-gateway/src/index.test.ts
  • packages/keiko-tools/src/index.test.ts
  • packages/keiko-memory-governance/src/version.test.ts
  • packages/keiko-harness/src/index.test.ts
  • packages/keiko-evidence/src/index.test.ts
  • packages/keiko-memory-consolidation/src/index.test.ts
packages/**/src/**

⚙️ CodeRabbit configuration file

packages/**/src/**: Enforce ADR-0019 package direction and the owning trust boundary. Flag provider SDK imports
outside keiko-model-gateway, cross-package wire types outside contracts, workspace escape,
raw evidence bodies, silent failures, and parallel subsystems that should extend an owner.

Files:

  • packages/keiko-memory-retrieval/src/version.test.ts
  • packages/keiko-memory-retrieval/src/index.test.ts
  • packages/keiko-tools/src/version.test.ts
  • packages/keiko-security/src/index.test.ts
  • packages/keiko-memory-governance/src/index.test.ts
  • packages/keiko-memory-consolidation/src/version.test.ts
  • packages/keiko-contracts/src/index.test.ts
  • packages/keiko-workspace/src/index.test.ts
  • packages/keiko-model-gateway/src/index.test.ts
  • packages/keiko-tools/src/index.test.ts
  • packages/keiko-memory-governance/src/version.test.ts
  • packages/keiko-harness/src/index.test.ts
  • packages/keiko-evidence/src/index.test.ts
  • packages/keiko-memory-consolidation/src/index.test.ts
**/*.test.{ts,tsx,mjs}

⚙️ CodeRabbit configuration file

**/*.test.{ts,tsx,mjs}: A behavioral fix needs a failure-first regression proof that fails without the fix. Cover
malformed, hostile, empty, and boundary inputs; never relax a regression pin or duplicate a
production formula inside a fixture.

Files:

  • packages/keiko-memory-retrieval/src/version.test.ts
  • packages/keiko-memory-retrieval/src/index.test.ts
  • scripts/__tests__/portable-runtime.test.mjs
  • packages/keiko-tools/src/version.test.ts
  • packages/keiko-security/src/index.test.ts
  • packages/keiko-memory-governance/src/index.test.ts
  • scripts/__tests__/registry-install-smoke-security.test.mjs
  • packages/keiko-memory-consolidation/src/version.test.ts
  • packages/keiko-contracts/src/index.test.ts
  • packages/keiko-workspace/src/index.test.ts
  • packages/keiko-model-gateway/src/index.test.ts
  • packages/keiko-tools/src/index.test.ts
  • packages/keiko-memory-governance/src/version.test.ts
  • packages/keiko-harness/src/index.test.ts
  • scripts/__tests__/zip-archive.test.mjs
  • packages/keiko-evidence/src/index.test.ts
  • packages/keiko-memory-consolidation/src/index.test.ts
  • scripts/__tests__/release-portable-prerelease.test.mjs
  • scripts/__tests__/portable-release-verification.test.mjs
  • scripts/__tests__/release-publish-pipeline.test.mjs
scripts/**

⚙️ CodeRabbit configuration file

scripts/**: Gate and evidence tooling must fail closed, remain deterministic and redacted, and carry
negative tests proving that weakened configuration or stale evidence is rejected.

Files:

  • scripts/__tests__/portable-runtime.test.mjs
  • scripts/__tests__/registry-install-smoke-security.test.mjs
  • scripts/__tests__/zip-archive.test.mjs
  • scripts/__tests__/release-portable-prerelease.test.mjs
  • scripts/release-publish.mjs
  • scripts/lib/portable-release-verification.mjs
  • scripts/lib/zip-archive.mjs
  • scripts/release-portable-prerelease.mjs
  • scripts/__tests__/portable-release-verification.test.mjs
  • scripts/__tests__/release-publish-pipeline.test.mjs
packages/keiko-model-gateway/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Provider SDKs such as openai, @anthropic-ai/*, and *-ai-sdk may be imported only by keiko-model-gateway.

Files:

  • packages/keiko-model-gateway/src/index.test.ts
.github/workflows/**

📄 CodeRabbit inference engine (AGENTS.md)

Never push directly to dev, force-push, bypass required checks, dismiss findings to obtain green status, or merge outside the ADR-0135 direct-check path.

Files:

  • .github/workflows/portable-assets.yml

⚙️ CodeRabbit configuration file

.github/workflows/**: Treat workflow changes as supply-chain and merge-authority changes. Require full 40-hex
action pins with version comments, minimum permissions, bounded timeouts, locked installs,
redacted output, and exact-current-head evidence. Never recommend bypassing a gate.

Files:

  • .github/workflows/portable-assets.yml
.github/workflows/**/*.{yml,yaml}

📄 CodeRabbit inference engine (AGENTS.md)

Pin every GitHub Action to a full 40-character commit SHA with a version comment; tag and branch references are prohibited.

Files:

  • .github/workflows/portable-assets.yml
🧠 Learnings (4)
📚 Learning: 2026-07-25T18:42:13.123Z
Learnt from: oscharko
Repo: oscharko-dev/Keiko PR: 2716
File: packages/keiko-tools/src/editor-agent-client.test.ts:692-692
Timestamp: 2026-07-25T18:42:13.123Z
Learning: In Keiko TypeScript test files, do not request explicit return type annotations for callbacks passed to typed Vitest `it(...)` and `it.each(...)`. Specifically, avoid adding `: void` or `: Promise<void>` to those callback functions when the ESLint rule `typescript-eslint/explicit-function-return-type` is configured with `allowTypedFunctionExpressions: true` (i.e., typed function expressions are intentionally exempt). Only ask for explicit return types if the ESLint configuration changes; otherwise preserve the existing surrounding test-file style.

Applied to files:

  • packages/keiko-memory-retrieval/src/version.test.ts
  • packages/keiko-memory-retrieval/src/index.test.ts
  • packages/keiko-tools/src/version.test.ts
  • packages/keiko-security/src/index.test.ts
  • packages/keiko-memory-governance/src/index.test.ts
  • packages/keiko-memory-consolidation/src/version.test.ts
  • packages/keiko-contracts/src/index.test.ts
  • packages/keiko-workspace/src/index.test.ts
  • packages/keiko-model-gateway/src/index.test.ts
  • packages/keiko-tools/src/index.test.ts
  • packages/keiko-memory-governance/src/version.test.ts
  • packages/keiko-harness/src/index.test.ts
  • packages/keiko-evidence/src/index.test.ts
  • packages/keiko-memory-consolidation/src/index.test.ts
📚 Learning: 2026-07-27T10:55:30.485Z
Learnt from: oscharko
Repo: oscharko-dev/Keiko PR: 2766
File: packages/keiko-server/src/qualityIntelligence/figmaSnapshotRoutes.test.ts:1738-1738
Timestamp: 2026-07-27T10:55:30.485Z
Learning: When reviewing TypeScript files, do not flag single-quoted string literals as a violation if they match Prettier’s formatter-approved output. Specifically, if Prettier is configured to prefer double quotes but retains a single-quoted literal solely because switching to double quotes would require escaping embedded double quotes, allow the single quotes (i.e., don’t “fix” it beyond what Prettier would produce).

Applied to files:

  • packages/keiko-memory-retrieval/src/version.test.ts
  • packages/keiko-memory-retrieval/src/index.test.ts
  • packages/keiko-tools/src/version.test.ts
  • packages/keiko-security/src/index.test.ts
  • packages/keiko-memory-governance/src/index.test.ts
  • packages/keiko-memory-consolidation/src/version.test.ts
  • packages/keiko-contracts/src/index.test.ts
  • packages/keiko-workspace/src/index.test.ts
  • packages/keiko-model-gateway/src/index.test.ts
  • packages/keiko-tools/src/index.test.ts
  • packages/keiko-memory-governance/src/version.test.ts
  • packages/keiko-harness/src/index.test.ts
  • packages/keiko-evidence/src/index.test.ts
  • packages/keiko-memory-consolidation/src/index.test.ts
📚 Learning: 2026-07-27T18:08:22.461Z
Learnt from: oscharko
Repo: oscharko-dev/Keiko PR: 2780
File: packages/keiko-server/src/store/migrations.test.ts:78-95
Timestamp: 2026-07-27T18:08:22.461Z
Learning: When reviewing TypeScript lint findings (e.g., from typescript-eslint/no-unsafe-assignment), ensure the issue is reproducible using the repository’s configured ESLint setup/TS project settings. Do not treat diagnostics observed under a mismatched TypeScript project/tsconfig as authoritative; only accept them as real if you can reproduce them with the same ESLint configuration the repo uses (e.g., via its full/targeted lint scripts). In related test code, prefer deriving branded identity types from the production function return types to keep RootIdentity/brand fields type-compatible and avoid unsafe assignments.

Applied to files:

  • packages/keiko-memory-retrieval/src/version.test.ts
  • packages/keiko-memory-retrieval/src/index.test.ts
  • packages/keiko-tools/src/version.test.ts
  • packages/keiko-security/src/index.test.ts
  • packages/keiko-memory-governance/src/index.test.ts
  • packages/keiko-memory-consolidation/src/version.test.ts
  • packages/keiko-contracts/src/index.test.ts
  • packages/keiko-workspace/src/index.test.ts
  • packages/keiko-model-gateway/src/index.test.ts
  • packages/keiko-tools/src/index.test.ts
  • packages/keiko-memory-governance/src/version.test.ts
  • packages/keiko-harness/src/index.test.ts
  • packages/keiko-evidence/src/index.test.ts
  • packages/keiko-memory-consolidation/src/index.test.ts
📚 Learning: 2026-07-26T21:23:56.288Z
Learnt from: oscharko
Repo: oscharko-dev/Keiko PR: 2755
File: packages/keiko-server/src/grounded-qa-hybrid.ts:1006-1012
Timestamp: 2026-07-26T21:23:56.288Z
Learning: In Keiko’s internal “stable-ID” hash helper functions (e.g., where stable identifiers are computed for persistence/comparison), preserve the existing UTF-16 code-unit iteration semantics when modernizing. Specifically, if the current implementation iterates with `charCodeAt(i)` (code units) rather than `codePointAt(i)` (Unicode code points), keep `charCodeAt(i)` to avoid changing the resulting stable IDs. If changing from `charCodeAt` to `codePointAt` is ever desired, it must be an explicitly planned compatibility/migration decision (e.g., versioning, backfill, or dual-read/write) rather than a mechanical refactor.

Applied to files:

  • packages/keiko-contracts/src/index.test.ts
🪛 LanguageTool
docs/adr/ADR-0130-npm-trusted-publishing-for-release-pipeline.md

[style] ~66-~66: Consider an alternative for the overused word “exactly”.
Context: ... npm@11.16.0 ``` The pinned version is exactly the governed EXPECTED_PACKAGE_MANAGER...

(EXACTLY_PRECISELY)

🔇 Additional comments (19)
scripts/lib/portable-release-verification.mjs (2)

466-479: The repository is now threaded through every artifact read.

declaredArtifactRecordVerified builds the path from the verified repository instead of letting gh expand {owner}/{repo} from the checkout remote, and scripts/release-publish.mjs forwards the same identity into the adapter. This resolves the earlier finding.

Also applies to: 509-519


162-168: 🔒 Security & Privacy

No change needed: manifest completeness already fails closed.

portableReleaseFailures stops before declaredDownloads when portableEvaluationManifestFailures(manifest, ...) reports missing assets, so the remaining checks never run for the omitted download.

			> Likely an incorrect or invalid review comment.
.github/workflows/portable-assets.yml (2)

46-71: The scope job fails closed, and the jq expression is safe.

The tag reaches jq through --arg, so a hostile tag name cannot alter the filter. A missing entry, a missing portableRuntimeArtifactContract, or an empty entries array all resolve to production=true. A missing or malformed release-impact.catalog.json makes jq exit non-zero, which fails the step under the default bash -e shell and leaves the run red instead of publishing unsigned assets.

Permissions, the 40-hex action pin with its version comment, and the 5-minute timeout all match the workflow rules for this path.


72-75: 🩺 Stability & Availability

No change needed. production-signing-preflight is not in RELEASE_REQUIRED_CHECKS, and evaluation-scoped tags skip production staging by design.

</verification Result>


<!-- cr-comment:v1:607b1053c58d504167f671a0 -->

</blockquote></details>
<details>
<summary>scripts/lib/zip-archive.mjs (1)</summary><blockquote>

`290-317`: **The structural guards and the descriptor lifetime are correct.**

`endOfCentralDirectoryOffset` scans backwards and cannot run past the buffer start, because a file shorter than 22 bytes produces a negative start offset and the loop body never runs. `readCentralEntry` bounds-checks its 46-byte header and validates the signature, so a bad `next` value throws on the following iteration. `readAt` treats a zero-length read as truncation. `extractZipArchiveEntries` closes the descriptor in `finally` on every path, and `normalizedEntryName` is called without `allowUnsafeEntryNames`, so a traversal name throws before `writeFileSync` runs.



Also applies to: 376-385, 415-418, 437-439

<!-- cr-comment:v1:42db1953f265faf9482fb217 -->

</blockquote></details>
<details>
<summary>scripts/__tests__/portable-release-verification.test.mjs (1)</summary><blockquote>

`607-823`: **The artifact-digest suite covers the hostile cases well.**

The cases pin a foreign artifact id, a renamed record, a foreign run id, an expired record, an unreadable record, a malformed archive, a fetch failure, temporary-directory cleanup, the same-named evidence-file case from the 0.3.1 outage, and a genuine digest disagreement. The rerun case at lines 701-723 throws when the mutable artifact listing is consulted, which pins the immutable-id contract directly.



Also applies to: 825-905

<!-- cr-comment:v1:115046423920c12b5681d476 -->

</blockquote></details>
<details>
<summary>scripts/__tests__/zip-archive.test.mjs (1)</summary><blockquote>

`236-330`: **The reader suite now pins each branch of `inflatedEntryData` and the tamper message.**

The tamper case asserts the declared-size-or-checksum message, the stored case exercises method 0 through hand-built bytes, and the unsupported case exercises method 12. The traversal case uses `allowUnsafeEntryNames` on the writer to produce a hostile archive, which keeps the fixture derived from the production writer.

<!-- cr-comment:v1:defa4f3eeec310e3e271377b -->

</blockquote></details>
<details>
<summary>docs/local-runtime-state-contract.md (1)</summary><blockquote>

`4-7`: **Include 0.3.2 in the unchanged-state range.**

Line 7 stops at `0.3.1`, although Lines 4-6 state that `0.3.2` changes none of this contract. State `0.3.0 through 0.3.2`.

<!-- cr-comment:v1:355bf68763e235a4b43c9cf4 -->

</blockquote></details>
<details>
<summary>scripts/__tests__/release-portable-prerelease.test.mjs (1)</summary><blockquote>

`493-505`: **Assert the evaluation manifest bindings.**

This test only checks that the manifest filename reaches `gh release create`. Snapshot and parse the temporary manifest. Assert its workflow attempt, immutable artifact IDs, and asset digests. A malformed manifest can otherwise reach the release while the npm publisher later refuses it.

<!-- cr-comment:v1:c389f070e57f9b22d7e36470 -->

</blockquote></details>
<details>
<summary>packages/keiko-tools/src/version.test.ts (2)</summary><blockquote>

`12-14`: **Update the stale test description.**

Line 13 says the test checks the literal `0.3.1`, but Line 14 compares `KEIKO_TOOLS_VERSION` with `packageVersion`. Rename the case to describe the manifest synchronization, such as `matches the packaged manifest version`. This finding was already raised in the previous review.

<!-- cr-comment:v1:05d49e8a2cfb318963b6651c -->

---

`2-10`: LGTM!

<!-- cr-comment:v1:11c3cd70304f6743b99bf693 -->

</blockquote></details>
<details>
<summary>packages/keiko-contracts/src/index.test.ts (1)</summary><blockquote>

`2-3`: LGTM!



Also applies to: 188-193, 200-201

<!-- cr-comment:v1:2fb8f07afb4a2f75869b0f9c -->

</blockquote></details>
<details>
<summary>packages/keiko-evidence/src/index.test.ts (1)</summary><blockquote>

`9-10`: LGTM!



Also applies to: 60-68

<!-- cr-comment:v1:b867c849455a7ccc8198f6fe -->

</blockquote></details>
<details>
<summary>packages/keiko-harness/src/index.test.ts (1)</summary><blockquote>

`13-14`: LGTM!



Also applies to: 66-76

<!-- cr-comment:v1:f538d65d8f97cefdf322bda2 -->

</blockquote></details>
<details>
<summary>packages/keiko-memory-consolidation/src/index.test.ts (1)</summary><blockquote>

`11-12`: LGTM!



Also applies to: 42-46, 52-54

<!-- cr-comment:v1:d2ebdaa0c4ae48b92b49439f -->

</blockquote></details>
<details>
<summary>packages/keiko-tools/src/index.test.ts (1)</summary><blockquote>

`8-9`: LGTM!



Also applies to: 106-114

<!-- cr-comment:v1:7423fb17d5bcfac1fa145e9d -->

</blockquote></details>
<details>
<summary>packages/keiko-workspace/src/index.test.ts (1)</summary><blockquote>

`8-20`: LGTM!

<!-- cr-comment:v1:ccc1a3b331c3ab4a0e58a89a -->

</blockquote></details>
<details>
<summary>packages/keiko-memory-retrieval/src/version.test.ts (1)</summary><blockquote>

`14-15`: **Rename the stale version in the test title.**

Line 14 still says `0.3.1`, while line 15 compares `KEIKO_MEMORY_RETRIEVAL_VERSION` with `packageVersion`. Rename the title to describe the package-manifest comparison. The previous review raised the same finding, and the stale text remains.

<!-- cr-comment:v1:f3400674e1b0fbcedd176618 -->

</blockquote></details>
<details>
<summary>packages/keiko-memory-consolidation/src/version.test.ts (1)</summary><blockquote>

`9-11`: _📐 Maintainability & Code Quality_

The unchecked manifest assertion no longer remains in the listed test files. If the review should also address helper reuse, inspect whether a shared test helper is available.

<!-- cr-comment:v1:602743416b247ac98847223b -->

</blockquote></details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment thread .github/zizmor.yml
Comment thread docs/adr/ADR-0130-npm-trusted-publishing-for-release-pipeline.md Outdated
Comment thread release-impact.catalog.json Outdated
Comment thread scripts/__tests__/portable-release-verification.test.mjs Outdated
Comment thread scripts/__tests__/registry-install-smoke-security.test.mjs
Comment thread scripts/__tests__/zip-archive.test.mjs
Comment thread scripts/lib/portable-release-verification.mjs
Comment thread scripts/lib/zip-archive.mjs
Comment thread scripts/lib/zip-archive.mjs
Comment thread scripts/release-publish.mjs
CodeRabbit findings on #3055:

- inflation is capped at the declared entry size; ZIP64 sentinels
  refuse in both readers; the extractor's own truncation path is pinned
- manifest-declared artifact names must be filesystem-safe tokens —
  they reach join() and API paths in the verifier
- the by-id zip fetch carries a deadline and the portable archive size
  ceiling
- the anchors checker position-checks adhoc-packages ignores (the
  release.yml pin drifted twice in one day unnoticed)
- the gate test pins all four collector-port arguments
- ADR-0130 drops stale 10.9.x references, records the governed-pin
  amendment in its version history; the catalog names the four
  downloads precisely

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@oscharko
oscharko temporarily deployed to keiko-for-quality August 10, 2026 05:20 — with GitHub Actions Inactive
Comment thread docs/adr/ADR-0130-npm-trusted-publishing-for-release-pipeline.md
Comment thread scripts/lib/portable-evaluation-manifest.mjs
Comment thread scripts/lib/zip-archive.mjs
@oscharko
oscharko temporarily deployed to keiko-for-quality August 10, 2026 05:22 — with GitHub Actions Inactive
…tion

- a non-string artifact name refuses before the regex can coerce it
  into a matching token (KfQ finding on #3055)
- the extractor bounds the declared compressed size against the file
  size before allocating the read buffer, so a hostile directory entry
  refuses as truncation instead of forcing a giant allocation (KfQ)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@oscharko
oscharko temporarily deployed to keiko-for-quality August 10, 2026 05:29 — with GitHub Actions Inactive
Comment thread scripts/lib/portable-release-verification.mjs
Comment thread scripts/lib/zip-archive.mjs
@oscharko
oscharko temporarily deployed to keiko-for-quality August 10, 2026 05:30 — with GitHub Actions Inactive
maxOutputLength alone still admits a 4 GiB allocation from a hostile
uint32 declaration; no legitimate entry exceeds the archive ceiling
(KfQ finding on #3055).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@oscharko
oscharko temporarily deployed to keiko-for-quality August 10, 2026 05:33 — with GitHub Actions Inactive
Comment thread scripts/lib/zip-archive.mjs
@oscharko
oscharko temporarily deployed to keiko-for-quality August 10, 2026 05:34 — with GitHub Actions Inactive
@oscharko
oscharko merged commit 2cfac8b into dev Aug 10, 2026
11 checks passed
@oscharko
oscharko deleted the claude/verify-artifacts-by-id branch August 10, 2026 05:44
oscharko added a commit that referenced this pull request Aug 16, 2026
…2900) (#3159)

* fix(arch): restore three dead trust rules and align config accuracy (#2900)

Audit findings KEIKO-0152, KEIKO-0255, KEIKO-0373, KEIKO-0387, KEIKO-0251,
KEIKO-0381.

KEIKO-0255: trust-1/4/5 targeted destinations outside the first-party
namespace (provider SDKs, node:fs, node:fs/promises). options.includeOnly
pruned those nodes before evaluation, so all three rules were structurally
dead — 0 dependencies cruised against their own fixtures. Enumerated those
destinations in includeOnly (never node_modules/.*, so the @oscharko-dev
scope stays outside the filter and the #2627 bare-specifier resolution is
untouched), added the fixture dirs to each rule's from.path, and widened
trust-1's to.path to also match the bare unresolved specifier: no provider
SDK is a dependency here, so a node_modules-only pattern can never match.
The three names are now pinned in EXPECTED_DEPCRUISER_RULE_COUNTS, and a
new superset assertion fails the gate if the fixture-scan override ever
drops below the production filter. check-import-policy.mjs keeps its
independent AST enforcement — this restores a layer, it does not replace one.

KEIKO-0373: 4a-harness, 3c-tools and 3e-local-knowledge did not forbid the
retired root src/<domain> shims of their own allow-listed dependencies,
unlike the four sibling rules governing structurally identical cases.

KEIKO-0152/0387: 5a-workflows, 6a-server and 7a-cli prose understated what
their own to.path permits (server by six packages). Comment-only.

KEIKO-0251: the root suite collected zero *.test.tsx, so `npm test` and
conversation:release-check went green without running the editor's eight
component tests, including two a11y suites and the GEN-PERF-EDITOR-005
allocation pin. vitest-config-parity.test.mjs now compares the collected
sets, not the pattern text.

KEIKO-0381: no-fractional-numeric-separators tested for "." and "_"
independently, condemning integer-part grouping such as 1_500_000.5 in six
files wired at severity error under --max-warnings=0.

Verified: arch:check, arch:check:negative (55 fixtures, was 52),
vitest-config-parity (19), sonar-preflight-eslint (5).

* fix(ci): close three governance gaps in severity, scanning and install scripts (#2900)

Audit findings KEIKO-0289, KEIKO-0314, KEIKO-0955.

KEIKO-0289: severity-gate.test.ts asserted severity only for rules named in
two hand-maintained allow-lists. Re-deriving its own matching logic found 5
of 35 rules covered by neither — connectors, sandbox, editor, and both
domain-not-server/cli (adr-0019-direction-6a-… does not satisfy
startsWith("adr-0019-direction-6-")). arch:check:negative could not have
caught a downgrade either: it counts a rule name's occurrences in stdout and
never reads the severity label, so a warn-only rule still fires, still
prints, and silently stops blocking. Severity is now enforced by exhaustion
over config.forbidden minus a named, empty exemption list; the allow-lists
stay as existence pins. The audit recorded 6 uncovered rules — keiko-git
(2b) was covered by other work since baseline acfc607.

KEIKO-0955: codeql.yml and dependency-review.yml had fallen nine and ten
branches behind ci.yml, so pushes to those integration branches were never
code-scanned and pull requests targeting them had no dependency diff
reviewed. Nothing ever went red because every such branch eventually merges
into dev, which was listed everywhere. New check-workflow-branch-parity.mjs
compares the three lists as sets and runs in the workflow hygiene job.

KEIKO-0314: npm's allow-scripts governance was unconfigured, so fsevents and
unrs-resolver ran lifecycle scripts unreviewed on every install behind a
warning that fails nothing. Both scripts read and recorded; .npmrc now sets
strict-allow-scripts=true (proven to fail npm ci closed) and package.json
records the approvals. npm 11.16.0 ignores a pinned name@version key for
unrs-resolver, so its npm-level approval has to be unpinned — which would
bless any future version. check-install-script-approvals.mjs closes that by
reading package-lock.json: every platform's tree, exact versions, proven to
catch both a version bump and a newly introduced script.

Verified: severity-gate (35), check-workflow-branch-parity (15 branches
across 3 workflows, and red on a reverted list), check-zizmor-anchors (8
anchors), check-install-script-approvals, check:qi-supply-chain,
check:workspace-supply-chain, npm ci clean under strict-allow-scripts.

* fix(templates,e2e): private security channel, honest check lists, real diagnostics (#2900)

Audit findings KEIKO-0140, KEIKO-0297, KEIKO-0311, KEIKO-0406, KEIKO-0429,
KEIKO-0499.

KEIKO-0297: the public user-finding form offered a "Data loss or security
concern" impact option with no route to the private channel, so a
well-intentioned reporter was guided to describe a vulnerability, its
reproduction and its evidence in a public issue — the exact outcome
SECURITY.md exists to prevent. Added the redirect to the form's intro and a
contact_links entry pointing at GitHub Security Advisories. The dropdown
option stays: a reporter who does not yet know a finding is security-relevant
still needs it.

KEIKO-0311/0499: both templates named `ci` as "the" required check (1 of 10)
and feature_task named Qodana, which this repository documents as not
configured. Both now defer to CONTRIBUTING.md rather than carrying a copy
that drifts, and the security-review item names `Keiko for Quality` and
`gates:sonar` — gates that exist.

KEIKO-0140: human-loop-1405.spec.ts renders hand-authored replica HTML, never
AgentGateCard, yet two design-system evidence docs cited it as proof of the
live component's scope rows. Deleting either row's copy from the component
would have been caught by nothing. Corrected both citations to say what the
spec actually proves (token/contrast resolution in a real browser engine) and
added the missing copy assertion to AgentGateCard.test.tsx, verified to fail
when the copy is changed.

KEIKO-0406/0429: two swallowed-diagnostic paths. The local-knowledge fixture
collapsed malformed bodies, over-size rejections and its own internal bugs
into one cause-less 400. The shared Code-task shutdown let a rejecting
teardown stage become a bare unhandled rejection with nothing naming which
resource failed, and an unclosed SSE connection hang until the harness killed
it. Both now name the failure on stderr; shutdown also tracks the stage,
exits 1 on failure, and bounds server.close() at 5s. Written with
process.stderr.write rather than console.error: `console` is not a declared
global for tests/e2e/fixtures/*.js and no-console is a warning under
--max-warnings=0, and widening either for one diagnostic line is the wrong
trade.

Not fixed here: KEIKO-0521 (release-impact catalog entry for Atlassian
Connectors). The finding still applies, but check-release-impact.mjs requires
an approvalReference resolving to a real release-owner GitHub artifact
carrying the Approved-for-publish phrase. Minting one would forge a
governance approval. Escalated.

Verified: AgentGateCard.test.tsx (3, and red on copy drift), eslint clean on
both e2e files, YAML parse on both templates, grep proofs for 0297/0499.

* feat(cli): ship the local-state audit as `keiko audit local-state` (#2900)

Audit finding KEIKO-0230; KEIKO-0235, KEIKO-0509 and KEIKO-0517 superseded.

KEIKO-0230: the at-rest contract names the local-state auditor as the
compensating control for the plaintext lexical-index disclosure, but the only
way to run it was `npm run audit:local-state`, which needs the monorepo
checked out. The audience that HAS a ~/.keiko tree to audit is precisely the
audience that does NOT have the monorepo, so the control was unreachable by
everyone who needed it.

Design decision (the finding records this as design-required): the auditor is
NOT ported to TypeScript. scripts/lib/local-state-audit.mjs is 1567 lines that
deliberately import only Node builtins and deliberately duplicate the on-disk
filename constants rather than take a package-graph edge — its header says so.
A TypeScript twin would double that surface and manufacture exactly the drift
it was written to avoid. Instead the new command imports the one existing
implementation at runtime and the module ships via the package `files`
allowlist (npm pack --dry-run: exactly one new file, 59.1kB, nothing else from
scripts/). The edge points cli -> script, so the script still takes no
package-graph edge and still runs standalone with no build. The bin entry
resolves the packaged path into KEIKO_LOCAL_STATE_AUDITOR, the convention
already used for KEIKO_UI_STATIC_ROOT and KEIKO_CLI_BIN_PATH.

Missing auditor fails closed with a named reason rather than printing nothing
and returning 0 — a silent skip reads as "audited, nothing wrong" for the one
control meant to prove the at-rest claims.

Verified end to end against the real fixtures through the BUILT CLI
(dist/cli/index.js), not just unit tests: healthy tree -> 7 PASS classes,
exit 0; drifted tree -> FAIL on credentials and file modes, exit 1; unknown
subcommand -> exit 2. Matches check-local-state.mjs's contract exactly.
check:package-surface:assembled green; arch:check green; 36 CLI tests pass.

KEIKO-0235/0509/0517 superseded, no code change: all three describe README
sections (Vision's "Keiko Twin" bullet, the "CLI essentials" table, the
"What's New" section frozen at 0.2.0) that commit 2cfac8b (PR #3055) removed
when it rewrote the README. grep for "Twin", "CLI essentials" and "What's New"
each return zero matches.

* fix(scripts): fail closed on stale pins, dead config paths and unbounded ANN latency (#2900)

Audit findings KEIKO-0157, KEIKO-0234, KEIKO-0286, KEIKO-0362.

KEIKO-0157: portable:approve-runtimes refreshed an OpenCode archive's sha256
and sizeBytes while carrying executableTreeSha256 — a digest of the EXTRACTED
tree — forward unchanged. When the bytes really changed, the written entry
paired a new archive digest with a tree digest describing the old contents,
and both this script and check:portable-approvals (JSON shape only, no
network) reported success. The mismatch surfaced at tag time in
portable-assets.yml, mid-release. It now fails at update time, before writing.
The existing test's fake archives were themselves a silent mismatch, so it is
split: one case re-downloads matching bytes and still carries the pin forward,
a new one proves changed bytes fail closed and leave the file untouched.

KEIKO-0286: ensureDevGatewayConfig skipped the seed whenever KEIKO_CONFIG_FILE
was merely SET. A stale value from a sourced operator .env therefore
suppressed the safety net exactly when it was needed, and the server degraded
to zero providers silently — the "no provisioned config" condition behind four
prior blocked live-test attempts. A configured path now earns the skip only if
the file exists. Repointing matters as much as seeding: the development runner
inherits this environment, so seeding while the dead path stayed in
KEIKO_CONFIG_FILE would have left the seed invisible to the server. The
decision is extracted as a pure exported helper (the file's existing
convention) and unit-tested, including that notices carry paths, never
contents.

KEIKO-0362: the ANN closeout proof asserted only relative latency — "ANN beat
exact". A build consuming 100s of its own 120s production timeout satisfied
that while having no headroom left. BUILD_TIMEOUT_MS/QUERY_TIMEOUT_MS are now
exported from usearch-ann-index.ts (one definition, imported, never
re-declared) and the proof enforces absolute ceilings against them. Fractions
calibrated openly against real measurements rather than picked: build 0.75
(90s) because the observed 35.9-38.8s local build would false-RED a required
CI gate at 0.5, query 0.5 (15s) against an observed 14ms median. Both recorded
in the code for the ADR-0164 D6 owner to tighten with CI-runner data.

KEIKO-0234: the gate named "retrieval-latency" measures only lexical
keiko-workspace searchText; ANN semantic retrieval (ADR-0164) has no latency
gate at all. Scope stated in the script header, the budget $comment and the CI
step name, so a green run can no longer be read as covering semantic retrieval.

Verified: portable-runtime-approvals (25), dev-start (28),
knowledge-m2-closeout (40), check:portable-approvals,
check:knowledge-m2-closeout (build 35.9s vs 90s ceiling, query 14ms vs 15s),
check:retrieval-latency, check:package-surface:assembled, eslint clean.

* fix(native): make the macOS runtime monitor observable and its reconcile answers distinguishable (#2900)

Audit findings KEIKO-0419, KEIKO-0433, KEIKO-0450.

KEIKO-0419: keiko_runtime_monitor.m emitted no diagnostic output anywhere, so
an Endpoint Security client that failed to start, a Full-Disk-Access refusal,
a rejected peer and the governance kill-switch SIGKILL fan-out were all
equally invisible — the operator saw only "not active". Added os_log under
subsystem com.oscharko.keiko.runtime-monitor at the decision points: every
endpoint_state transition (including the one in start_endpoint_security that
assigns under the lock and so bypasses the setter), the kill-switch fan-out,
the session process-budget kill, the es_new_client/es_subscribe failure
branches, and rejected client connections. Deliberately NOT in
endpoint_event(), which runs per fork/exec/exit machine-wide — logging there
would recreate the hot-path cost the O(n)-scan finding warns about. Redacted
per AGENTS.md §7: pids, counts, states and error codes only; a test asserts no
os_log line can reference argv, environ, cmdline or an executable path.

KEIKO-0450: every non-zero exit of the activation CLI discarded its reason,
including the NSError macOS supplied. Approval timeout, OS-reported failure
and "approved but the daemon never came up" now each say which one happened —
three different operator actions that used to look identical. Success paths
stay silent because portable-macos-activation.ts requires empty stderr there,
and a test pins that.

KEIKO-0433: RECONCILE answered KEIKO_MONITOR_ZERO_LIVE both for "no such
session" and for "found, but a live connection already owns it" — opposite
situations demanding opposite responses. Appended
KEIKO_MONITOR_ALREADY_ACTIVE = 9 (never renumbering, the wire protocol is
shared with built binaries), split the RECONCILE branch into its three real
outcomes, and gave keiko_runtime_supervisor.c its own
ERROR_TREE_ALREADY_SUPERVISED branch — without it the new code would have
fallen into the same generic ERROR_TREE_OBSERVE and been exactly as opaque as
the collapse it fixes. An owner mismatch deliberately stays
indistinguishable from an unknown handle: telling a different uid that the
handle exists is an information leak, not a diagnostic.

Verified: check:native:macos PASS (clang -Werror + static analyzer) after each
change; macos-system-extension-manager tests 16 pass, including the updated
assertion for the guard KEIKO-0433 split apart.

* fix(native): bound the pre-handshake read on accepted monitor connections (#2900)

Audit finding KEIKO-0283.

An accepted connection that never wrote its handshake blocked serve_client's
first read forever while holding one of the 32 connection slots. A same-team
peer that stalls — a crashed supervisor, a debugger-suspended process — could
therefore exhaust the budget and lock out every legitimate ARM and RECONCILE,
with the governance kill-switch among the casualties. No timeout existed
anywhere on the daemon side.

SO_RCVTIMEO/SO_SNDTIMEO are now applied to each accepted descriptor beside the
existing SO_NOSIGPIPE, and serve_client lifts them the moment the handshake
validates. That split is the point: the long-lived read after the handshake is
the deliberate dead-man's switch watching supervisor liveness, and a timeout
left on it would tear down healthy sessions every few seconds. Five seconds
catches a peer that never writes at all rather than one that is merely slow —
the client's own connect timeout is one second.

Verified: check:native:macos PASS (clang -Werror + static analyzer);
macos-system-extension-manager 18 tests pass, two new ones pinning both halves
(the bound is applied at accept, and it is cleared before the dead-man's-switch
loop).

* test(native): require the file-generation suite to observe a real read (#2900)

Audit finding KEIKO-0446.

assertKnownGeneration returned void on every non-zero status, and nothing
anywhere in assertFileGenerationConsistency required a single read to succeed.
The suite therefore proved only "no read returned WRONG bytes", never "reads
work" — a helper answering KSR_ACCESS_DENIED to all 32 concurrent reads passed
it unchanged. This is the release path: assertExternalBinaryConsistency runs
against the signed binary in the portable-assets lane.

assertKnownGeneration now reports whether the read observed a known
generation, and the suite asserts at least one of CONCURRENT_CONSISTENCY_READS
did. The floor stays at "at least one" deliberately: the target is a plain
regular file replaced only by atomic rename, so a correct helper must succeed
on a substantial fraction, and an unreachable floor would itself be the
regression this guards against. assertAncestorAliasConsistency is deliberately
left alone — its ancestor is legitimately absent or aliased for most of the
read window, so an all-failure run is valid there.

Verified: check:native:macos PASS with the real helper, so the floor is
reachable rather than aspirational. The stub-binary negative proof the finding
describes needs a stub that also satisfies the earlier protocol cases before
it reaches this suite; the audit did not run that proof either, and it is not
claimed here.

* fix(gates): repair the six shape pins this branch's own changes tripped (#2900)

Found by running the full suite before opening the PR, not by CI. Each is
repaired at the pin, never by relaxing it.

- workflow-hygiene-job.test.mjs pins the bundled job's step list in order,
  because the actionlint step's guard exemption depends on nothing but the
  checkout running ahead of it. The new branch-parity step is added to
  GATE_STEPS in its real position, and it already carries the same
  independent-reporting guard the assertion below it requires.

- dependency-security-config.test.mjs matches /pull_request:\n\s+branches:/,
  which my KEIKO-0955 comment split apart. The comment moved above `on:`,
  where it says the same thing without sitting between the two keys.

- Five zizmor anchors (ci.yml:412/502/568/647/733) shifted by three lines
  when the KEIKO-0314 step and the KEIKO-0234 comment landed above them.
  This is exactly the failure AGENTS.md §3 documents — a line anchor silently
  dropping a reviewed risk acceptance — and check:zizmor-anchors caught it and
  printed the corrected numbers. Re-pinned to 415/505/571/650/736.

- check-version-consistency pins src/cli/index.ts by content hash so root
  facade drift is reviewed rather than incidental. The facade legitimately
  gained a third installation-dependent path (KEIKO_LOCAL_STATE_AUDITOR,
  beside KEIKO_CLI_BIN_PATH and KEIKO_UI_STATIC_ROOT) — that is the facade's
  stated job, and the alternative would be teaching keiko-cli its own
  packaged layout, which the facade exists to prevent. Hash re-pinned to the
  reviewed content with the reason recorded at the pin. This also cleared the
  two release-impact-notes failures and the release-script-lcov-mapping
  failure, which were downstream of the same gate.

- The coverage baseline's keiko-cli file inventory was one behind after
  audit.ts. Updated from a real `test:coverage:packages` + `test:coverage:ui`
  run. Deliberately NOT regenerated wholesale: `--write-baseline` rewrote the
  whole file, deleting all 47 `fileFloors` per-file absolute floors (336 lines)
  — that is a ratchet, and stripping it to fix a file count would be exactly
  the "rewriting a pin to bless what it was written to prevent" AGENTS.md
  names as the highest-consequence artifact this repo can produce. Only the
  packages.keiko-cli snapshot changed; all 47 floors and all 25 package
  entries verified intact.

Verified: npm test 30367 passed / 1 failed before this commit -> the remaining
failure is the coverage inventory this commit fixes; check-package-coverage 54
pass, workflow-hygiene + dependency-security + zizmor-workflow +
ci-test-gate-wiring + registry-install-smoke 222 pass, check:zizmor-anchors 8
anchors PASS, check:version-consistency PASS, check:release-impact PASS,
format:check PASS.

* fix(scripts): use indexOf for the exact-match trigger lookup (#2900)

gates:sonar (javascript:S7753) on the new branch-parity gate: the callback
compared for equality, which is what indexOf already does.

The run reports one remaining finding, typescript:S7785 on src/cli/index.ts:58
("prefer top-level await over a promise chain"). It is NOT fixed here, and
deliberately so: those lines are unchanged from origin/dev (verified by diff)
and only entered scope because this branch edits the comment block above them.
SonarCloud's CI job scores new code, so it will not report them. Converting
the bin entry to top-level await would move process.exit from a microtask into
module evaluation, which tests/cli-root-entrypoint.test.ts observes through its
`await import("../src/cli/index.js")` — a behavioural change to the product
entry point, in an audit-remediation PR, to silence a finding on lines this PR
never touched.

* fix(test): narrow the severity-gate destructure for noUncheckedIndexedAccess (#2900)

CI's Core quality and both cross-platform smoke jobs failed on
TS18048 — 'first' is possibly 'undefined' — in the KEIKO-0289 test added
earlier on this branch.

`npm run typecheck` did not catch it: it runs `tsc -b tsconfig.json` through
the package graph, while CI type-checks the root project including `tests/**`.
Verified the fix the way CI does, with `npx tsc --noEmit -p tsconfig.json`.

Narrowed with an explicit guard rather than a non-null assertion: an empty
`config.forbidden` would make the assertion vacuous, so failing loudly there is
the behaviour worth having anyway.

* test(scripts): cover the two new gates so new-code coverage clears the bar (#2900)

SonarCloud's quality gate failed on new_coverage 46.3% against an 80%
threshold — not on issues. The two gates this branch added
(check-workflow-branch-parity.mjs, check-install-script-approvals.mjs) ran
everything at import time, so they were unreachable by a harness and
contributed ~230 uncovered new lines between them.

Both restructured into the shape every sibling gate in scripts/ already uses —
exported pure functions plus a `pathToFileURL(process.argv[1])` main guard —
and given harness tests under scripts/__tests__/. The tests are not coverage
padding: they pin the behaviour each gate exists for, including the branch
parser stopping at a sibling key rather than swallowing the next block, set
comparison so reordering is not drift, ci.yml disagreeing with itself, a
follower declaring no branch list at all, and — for the install-script gate —
the version-bump case that is the entire reason it reads the lockfile instead
of trusting npm's unpinned approval.

Coverage after: check-install-script-approvals 88.2% lines / 84.6% branches,
check-workflow-branch-parity 84.3% / 87.5%.

Also fixed while here: `URL` is not a declared global for
scripts/__tests__/*.mjs (used import.meta.dirname + node:path instead), and
findInstallScriptApprovalProblems exceeded the complexity ceiling, so the
per-package decision tree moved into its own function.

Verified: 25 new tests pass, both gates still PASS against the real repository,
check:zizmor-anchors PASS, eslint --max-warnings=0 clean.

* fix(arch): restore @anthropic-ai/ matching in the provider-SDK trust rule (#2900)

Review finding on this PR, and correct. When KEIKO-0255 widened trust-1's
to.path to also catch the bare unresolved specifier, it appended a shared
`($|/)` boundary to the whole alternation. That boundary is right for `openai`
(it stops `openai-adjacent` matching) but wrong for `@anthropic-ai/`, which
already ends in the separator — requiring another one made the scoped Anthropic
SDK unmatchable in the very rule that exists to forbid it.

Proven before: `@anthropic-ai/sdk` -> false, `node_modules/@anthropic-ai/sdk/
index.js` -> false. After: both true, `openai` and `vendor-ai-sdk` still true,
`openai-adjacent` still false. Each alternative now carries its own boundary.

Applied to the rule and to arch-check-negative's includeOnly override together,
so the two cannot disagree.

Verified: arch:check PASS, arch:check:negative PASS on 55 fixtures.

* chore(quality): classify .npmrc so the reviewer can complete its inventory (#2900)

Keiko for Quality settled this PR as `inventory.unclassified_path` and reported
it as NOT reviewed. Cause: `.npmrc`, added for KEIKO-0314, matched neither
`reviewRelevant` nor any `excluded` rule in .github/keiko-for-quality.json, and
src/inventory/inventory.ts records that reason code for any undescribed path.
Verified with the profile's own globs: of the 53 changed paths, `.npmrc` was the
only one unclassified; it is now 0.

Classified as review-relevant rather than excluded, deliberately. `.npmrc` is
supply-chain configuration — `strict-allow-scripts=true` is the control that
makes `npm ci` fail closed on an unreviewed lifecycle script — so a change to it
is exactly the kind of thing that should be read, not skipped. The committed
file holds that one setting and comments; it carries no auth token, and the
inventory is built from committed changes only, so no developer's local .npmrc
is ever in scope.

The profile hash feeds the reviewer's cache identity, so this also forces a
fresh review run rather than reusing the incomplete one.

* fix: address the review findings that were real defects in this branch (#2900)

Seven findings from the #3159 review, each reproduced before fixing.

check-install-script-approvals.mjs — the locked-package Map was keyed by name,
so two entries for the same package at different lockfile paths collapsed into
one: `node_modules/pkg@1.0.0` and `node_modules/other/node_modules/pkg@9.9.9`
gave lockedCount 1. In a supply-chain gate that is the wrong direction to lose
information in — the survivor could be the reviewed copy while an unreviewed
transitive one went unmentioned. Keyed by name@version; reproduced at 1, now 2.

coding-runtime-server-shared.mts — the close deadline RESOLVED, so a socket
that never closed fell through to exit(0). That turned the hang the bound
exists to surface back into a silent clean shutdown: KEIKO-0429's own defect,
one layer down. It now rejects and the stage handler exits 1.

eslint.config.js — `1.0e1_0` was flagged. Its fraction is a bare `0`; the
separator groups the EXPONENT. That is KEIKO-0381's mistake repeated one field
over, with a message naming a fractional separator that is not there. The check
now cuts at the exponent marker. Two acceptance cases added, plus one proving
`1.234_5e10` is still caught.

local-knowledge-e2e-server.js — my own comment claimed "log the Error only,
never the request body", which the code did not guarantee: V8's JSON.parse
echoes the input it choked on (`Unexpected token 'o', "not-json" is not valid
JSON`), so a credential in a malformed body would have reached the CI log.
Verified by reproduction. Now logs the error TYPE and the throwing frame, which
is the whole "client vs fixture" signal, and reproduces nothing sent.

audit.ts — the dynamically loaded auditor was trusted on shape. A missing
`classes` made renderReport throw; a truthy non-boolean `ok` printed PASS. The
one command whose job is proving the at-rest claims could announce a clean tree
it never read. Module and result are now validated, with five malformed-input
cases covering it.

audit.ts — `keiko audit local-state` ignored KEIKO_STATE_DIR and audited
<cwd>/.keiko, so an operator who moved the state tree got a PASS on the wrong
directory. Explicit --state-dir still wins over both.

ci.yml — bounded the new install-approvals step at 1 minute; it reads two local
JSON files and makes no network call.

Verified: 48 targeted tests pass, tsc --noEmit -p tsconfig.json clean, eslint
--max-warnings=0 clean, format:check clean, check:zizmor-anchors PASS.

* fix(native): make the containment proof real and guard both Windows harness paths (#2900)

Audit findings KEIKO-0261, KEIKO-0270, KEIKO-0278.

KEIKO-0270: the harnesses assert "Job Object must report zero active
processes" against proof+4 — four bytes both producers hard-coded to zero.
Windows wrote a literal 0; macOS never wrote them at all, so they stayed at
their initialiser. The assertion read a constant the producer chose, not an
observation, and could not fail. Took option (a): the producers now emit what
they observed. Windows returns accounting.ActiveProcesses out of
wait_for_zero_active (previously discarded) and writes it; macOS writes the
monitor's reply.live_processes. Both default to UINT32_MAX so a path that
skips the observation cannot read as a clean zero.

KEIKO-0278: qualifyWindows had a DEADLINE_MS timer but no try/finally, and
assertControlEofFailsClosed had no watchdog at all — the probe whose entire
purpose is proving the supervisor fails closed on control EOF, whose most
likely failure is a supervisor that hangs instead. Both now carry the same
guard the macOS qualify has: completion flag, finally, clearTimeout, SIGKILL
when incomplete, awaited exit. Pinned for both by name.

KEIKO-0261: the macOS source contract pinned three protocol constants and one
negative regex. It did not pin the fd-3/fd-4 close that keeps the supervised
runtime off the supervisor's own control and response pipes — and the
qualification fixture never touches those descriptors, so nothing behavioural
would notice their removal. Added both closes, the non-PATH posix_spawn form,
and extended the negative to reject posix_spawnp/execvp/execlp/execvP. Kept
above the darwin guard so it runs on every platform, as the finding requires.
A second pin asserts the supervisor source still satisfies what the harness
asserts, so the pair cannot drift into agreeing about nothing.

Verified: check:native:macos PASS (clang -Werror + analyzer),
macos-system-extension-manager 22 tests pass, eslint --max-warnings=0 clean.

* fix: second review round — redaction, domain checks and parity coverage (#2900)

Nine further findings from #3159, each checked against the code before acting.

Accepted and fixed:

- Branch parity missed workflow-hygiene.yml, which carries the same 15-branch
  list and is itself a required context. A branch absent there runs no
  actionlint, no pinned-SHA check, no zizmor and no OSV scan. Now 4 workflows.
- `keiko audit` and the Code-task shutdown both wrote `error.message`. Same
  class as the e2e fixture fixed last round: the auditor reads the state tree
  and the disposing resource holds payloads, so the text can quote what they
  were handling. Both now report the error TYPE; the shutdown already names
  the failing STAGE, which is the actionable half.
- The audit result's `status` was type-checked but not domain-checked, and
  renderReport prints an unrecognised status verbatim — an operator would have
  seen an unlabelled row that reads neither PASS nor FAIL. Now checked against
  the TAG vocabulary.
- `resolveDevGatewayConfigAction` only reported "nothing to seed" when a stale
  KEIKO_CONFIG_FILE was set. The plain first-run shape — nothing configured,
  nothing to seed — still started an unprovisioned gateway in silence, which
  is the condition KEIKO-0286 exists to end. Now said unconditionally.
- The 3e-local-knowledge comment still claimed `src/workspace/` was
  deliberately absent from the forbidden shim list; KEIKO-0373 added it. The
  comment now records why both it and `src/gateway/` are forbidden as shims
  while allow-listed as packages.
- `keiko audit --help` did not mention KEIKO_STATE_DIR, which now selects the
  default target.
- The retrieval-latency budget's scope note said what the gate does not cover
  but not that it implies nothing about ANN latency in either direction.

Refused, with the reason recorded in the header:

- Bumping KEIKO_MONITOR_VERSION for the new ALREADY_ACTIVE response. An older
  supervisor already routes unrecognised replies into ERROR_TREE_OBSERVE, so
  it fails closed on the one narrow path. Bumping would make the daemon reject
  every v1 supervisor outright — a hard break, to avoid a soft fail-closed
  degradation.

Verified: 57 targeted tests pass, check:native:macos PASS, arch:check PASS,
arch:check:negative 55 fixtures, check:zizmor-anchors PASS,
check-workflow-branch-parity PASS across 4 workflows, tsc --noEmit clean,
lint --max-warnings=0 clean, format:check clean.

* fix: third review round — hold the coverage ratchet, derive the scan filter (#2900)

KEIKO-0230 coverage ratchet. The reviewer was right to challenge it: the
baseline update had recorded keiko-cli statements 89.70 -> 89.52 and functions
92.56 -> 92.42, and a snapshot written below the previous values is a floor
lowered by omission. Chased it with tests rather than by accepting the number:
covered the real dynamic-import path (every prior case injected loadAuditor, so
the loader production actually uses was never executed), --help, and the
remaining asAuditResult rejection branches. audit.ts now has no uncovered
statement.

Result against the pre-PR values: lines 91.37 -> 91.45, branches 83.71 -> 84.01,
functions 92.56 -> 92.56, statements 89.70 -> 89.64. The judged metric is lines
(`metric: "lines"`, target 85) and it rose; statements is not ratcheted and its
0.06 shortfall sits inside the baseline's own documented 0.10-point noise
allowance. All 47 fileFloors untouched. Recorded from measurement, not edited.

Also from this round:

- arch-check-negative restated the production includeOnly beside it. A copy
  drifts silently: production could admit a destination this copy lacks and the
  fixture scan would prune it while every assertion stayed green — the rule
  dead exactly the way KEIKO-0255 found trust-1/4/5 dead. The override is now
  DERIVED from the production regex.
- Three surfaces claimed ANN retrieval latency has no gate. KEIKO-0362 made
  that false inside this same PR. ci.yml, the script header and the budget
  $comment now point at check:knowledge-m2-closeout instead.
- The activation manager echoed argv[1] into stderr, which is captured into
  activation logs; a wrapper can pass a path or token by mistake. Fixed usage
  line only.

Verified: check-package-coverage PASS (25 packages), 54 baseline-guard tests,
20 audit tests, arch:check:negative 55 fixtures, check:native:macos PASS,
format:check and lint clean.

* fix(ci): re-pin the zizmor anchors after the ANN-comment edit (#2900)

The one-line comment correction in ci.yml's retrieval-latency step shifted
every anchor below it by one. Three CI contexts failed on the same single
cause: `workflow hygiene`, `Coverage shard (packages 3/3)` and
`Coverage suite (scripts)` — the latter two through
check-zizmor-anchors.test.mjs, which pins the same invariant from the suite.

Re-pinned 415/505/571/650/736 -> 416/506/572/651/737, applied in the loop the
gate itself prescribes: run, take the corrected numbers it prints, repeat until
PASS. My own mistake for editing ci.yml in the previous commit without re-running
check:zizmor-anchors afterwards — the gate did exactly what AGENTS.md §3 says it
is for, twice on this branch now.

Verified: check:zizmor-anchors PASS (8 anchors), check-zizmor-anchors 13 tests,
format:check clean.

* fix: fourth review round — close the approvals loop both ways (#2900)

- check-install-script-approvals only checked reviewed -> npm. The other
  direction was open: `npm approve-scripts <pkg>` alone let a lifecycle script
  run with no recorded review, and this gate still said PASS. Every allowScripts
  entry must now carry a REVIEWED_INSTALL_SCRIPTS record. A record enforced in
  one direction only is not a record.
- asAuditResult accepted two shapes it should not: an empty class list, which
  renders an empty report and prints PASS for a tree nothing was checked
  against, and `ok: true` alongside a failing class, where the headline verdict
  would hide the finding. Both rejected, both covered.
- audit.test.ts left its temporary auditor directory behind.
- The runtime-state contract now documents the
  --state-dir / KEIKO_STATE_DIR / cwd resolution order beside the command.

Verified: 34 tests across the two suites, check:install-script-approvals PASS,
check:zizmor-anchors PASS, tsc clean (0 errors outside the two pre-existing
contextAssembly ones inherited from dev), lint --max-warnings=0 and
format:check clean.

* fix: owner check before liveness, and escape control chars in the audit report (#2900)

Two review findings, the first a security regression this branch introduced.

RECONCILE ordering. My KEIKO-0433 split tested `descriptor >= 0` before
`session->uid != uid`, so a same-team process under a DIFFERENT uid could
present a valid handle for a connected session and receive
KEIKO_MONITOR_ALREADY_ACTIVE — confirming the handle exists. That is exactly
the leak the comment two lines below claims to prevent, defeated by the
sequence. The owner check now runs first: a non-owner cannot distinguish
"live", "torn down" and "never existed". The comment was right; the code did
not implement it.

Audit report escaping. The production auditor puts filesystem-derived names
into `findings`, and renderReport wrote them verbatim. A crafted artifact name
containing a newline could forge a report line — including a `=> PASS` — and an
ESC could repaint the verdict in the operator's terminal. C0, DEL and C1 are
now escaped, proven by a test that feeds exactly that payload.

Verified: check:native:macos PASS (clang -Werror + analyzer),
macos-system-extension-manager 22 tests (the KEIKO-0433 pins still hold after
the reorder), audit 23 tests, tsc clean, lint and format:check clean.

* fix(cli): use String.raw for the control-character escape prefix (#2900)

SonarCloud typescript:S7780, the single unresolved new-code issue on this PR.
The escaping helper built its `\x` prefix with a doubled backslash inside a
template literal; String.raw states the intent without the escape.

The local gates:sonar run still reports typescript:S7785 on
src/cli/index.ts:58, and that one stays unfixed on purpose: those lines are
unchanged from origin/dev, SonarCloud's new-code analysis does not report them
(its issue list for this PR is now empty), and converting the bin entry to
top-level await would move process.exit from a microtask into module
evaluation, which tests/cli-root-entrypoint.test.ts observes through its
`await import`.

Verified: audit 23 tests, eslint --max-warnings=0 clean, format:check clean,
SonarCloud quality gate already reports status OK for this PR.
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