Skip to content

feat: add native plugin capability tour with lifecycle hook proof - #34

Merged
myagentdojo merged 15 commits into
mainfrom
feat/native-plugin-capability-tour
Aug 10, 2026
Merged

myagentdojo merged 15 commits into
mainfrom
feat/native-plugin-capability-tour

Conversation

@myagentdojo

@myagentdojo myagentdojo commented Aug 10, 2026 •

Copy link
Copy Markdown
Owner

Summary

The installed plugin is now a guided capability tour instead of a generic skill bundle. An evaluator can install one plugin in Claude Code or Codex, decide hook trust, and run the tour to learn which portable plugin capabilities actually work in their client — with every claim labeled by the evidence that backs it. Nothing from v0.2.0 regresses: all four portable skills, the three Bun launchers, and runtime custody ship unchanged.

Implements docs/plans/native-plugin-capability-tour-plan.md (U1-U4). Session-settled decisions carried from planning: one capability tour extending the same plugin (user-directed, over a second proof plugin); agentless delegation via a skill-local reviewer prompt (user-approved, over a packaged agent or Codex companion); no MCP in v0.3.0 (user-approved); plugin-owned drift fixture (user-directed, over executing workspace checks); visible success once at session start (user-directed).

Surface What is now possible
Lifecycle hooks SessionStart emits one identity receipt; Stop silently compares a packaged fixture byte-for-byte and blocks only on proven drift, once, then yields. Everything ambiguous fails open. No Bun/Node/Python/jq — POSIX sh under a system-only PATH.
Capability tour A model-only skill renders a four-group health report (verdict, evidence matrix, portable skills, next action) and seeds one bounded read-only subagent from a skill-local prompt, with truthful delivery labels and an inline fallback.
Identity One canonical config projects display name, description, starter prompt, brand color, and neutral SVG assets into both clients' manifests; initialized templates regenerate the identity, including hook receipts and fixture copy.
Proof honesty Package/distribution/release proofs bind to candidate commit + archive checksum + payload hashes, admit only an exact expected inventory, and report native activation and delegation as not-proved until human-operated fresh-client receipts exist. Ship-canary now binds candidate lineage before qualification is emitted.

Design decisions worth review attention:

  • The Stop guard extracts only top-level stop_hook_active with a narrow awk lexical scan (linear-time, 1 MiB cap) — deliberately not a JSON parser. String/nested decoys, duplicate keys, and oversized input are all covered by tests.
  • Hook declarations are generated per client around one shared handler, and the plugin version never appears in declarations, so a version-only release cannot churn Codex's hook-definition trust hash (locked by projection tests).
  • currentSessionHook derives only from an explicit current-session marker; external qualification receipts cannot flip it. Direct handler execution never satisfies a native-activation field.

Validation

Full Bun suite: 630 pass / 0 fail (three consecutive runs at the final head). generate:check, build, package, prove:distribution, prove:runtime-custody, prove:runtime-platform (darwin-arm64), release:validate --json, and prove:all all green; git diff --check clean. Hook behavior verified against isolated temp plugin roots with byte-level tree snapshots (no-mutation proof), hostile cwd/PATH, and a timing regression bound on ~1 MiB hostile payloads. Multi-lens code review (correctness, security, testing, maintainability, agent-native, plus an independent cross-model adversarial pass) returned five actionable findings, all applied and re-verified in d649319 — including an empirically reproduced payload-closure gap and a quadratic awk scan measured at ~70s/MiB, now 0.5-1.1s.

Known Residuals

  • Decision needed (P2, cross-model, confidence 100): the narrow Stop scan can extract a valid stop_hook_active from JSON that is malformed elsewhere (e.g. a missing comma between other members), so a real fixture mismatch still blocks on such input. Broader top-level grammar validation would fix it but tensions with the plan's no-general-JSON-parser decision. plugin/hooks/native-capability-hook:264.
  • Advisory: the bounded stdin read loop spawns a dd/wc pair per short read; a slow-drip producer causes many process spawns (degradation only; fail-open preserved).
  • Advisory: the awk scanner remains duplicated (identically) across the hook's two awk programs; consolidation must respect the dependency-free constraint.
  • Advisory: scripts/prove-harness-install.ts is 1900+ lines and should eventually split.
  • Outstanding by design: fresh native Claude/Codex qualification receipts (R14-R16) are human-operated; the evidence envelope and lineage tooling shipped ready for them.

Post-Deploy Monitoring & Validation

No production runtime impact — this is a template repository. Validation is hosted CI on this PR (must be green) plus the human-operated fresh-client qualification documented in README/ADR 0008 before any v0.3.0 release tag. Failure signals: red hosted checks, or a qualification receipt whose lineage hashes do not match the candidate. Mitigation: fix on this branch before merge; no rollback surface exists.

New concepts

Fail-open lifecycle drift proof. A plugin ships two copies of one small fixture — a canonical source and a projection generated at build time — and its Stop hook compares their exact bytes at session end. A mismatch is the only condition that blocks (once, with a fixed reason); every operational ambiguity — malformed input, missing fixture, unreadable version, absent tooling — exits successfully with at most one bounded warning. Chosen over asserting workspace state because an automatic hook must never execute or judge user project code: the plugin can only make honest claims about bytes it shipped. This PR exercises it end to end: generation owns the projection (scripts/native-capability-fixture.ts), the handler only compares (cmp on shipped bytes), and re-entry with stop_hook_active: true is silent so one Stop attempt never loops. The boundary: this proves lifecycle mechanics, not integrity or security — don't reach for it as a tamper-detection or workspace-quality gate.


Compound Engineering

Summary by CodeRabbit

  • New Features
    • Added a guided native capability tour for Claude and Codex.
    • Added client-specific session start and stop lifecycle checks.
    • Added Codex branding, interface metadata, and read-only capability review with fallback behavior.
  • Bug Fixes
    • Improved package validation and deterministic archives to detect unexpected or altered content.
    • Added stronger installation, payload lineage, and qualification evidence checks.
  • Documentation
    • Expanded lifecycle, capability boundaries, platform support, and qualification guidance, including fail-open behavior and proof-only limitations.
    • Clarified that native behavior is currently qualified only through fresh client evidence on supported POSIX platforms.

@myagentdojo
myagentdojo temporarily deployed to hosted-canary-qualification August 10, 2026 00:32 — with GitHub Actions Inactive
@coderabbitai

coderabbitai Bot commented Aug 10, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This change adds a Claude and Codex capability tour with native lifecycle hooks, deterministic fixtures, bounded payload validation, deterministic packaging, installed capability evidence, and candidate-bound qualification lineage.

Changes

Native capability tour

Layer / File(s) Summary
Tour contract and client declarations
CONTEXT.md, README.md, docs/adr/*, docs/plans/*, plugin.config.json, plugin/.claude-plugin/*, plugin/.codex-plugin/*, plugin/hooks/*, plugin/skills/capability-tour/*
Documents the capability-tour contract and adds Claude/Codex manifests, hook declarations, lifecycle fixtures, capability skills, reviewer guidance, and scope boundaries.
Lifecycle hook mechanics and fixtures
plugin/hooks/native-capability-hook, scripts/native-capability-fixture.ts, scripts/native-capability-hook.test.ts, scripts/generate.ts, scripts/init.ts, scripts/dev.ts
Adds bounded lexical JSON and semver validation, fail-open lifecycle handling, fixture generation, recursion protection, development watchers, hostile-environment checks, and lifecycle tests.
Payload contracts and deterministic packaging
scripts/build.ts, scripts/plugin-config.ts, scripts/plugin-files.ts, scripts/package.ts, scripts/*surface.test.ts, scripts/*manifest*.test.ts, scripts/runtime-custody-generation.test.ts
Allows the declared capability payload, validates manifests and assets, checks exact inventories, preserves runtime catalogs, and creates deterministic archives.
Installed capability evidence
scripts/prove-distribution.ts, scripts/prove-dx.ts, scripts/prove-harness-install.ts, scripts/prove-harness-install.test.ts
Adds installed payload hashes, capability evidence, receipt validation, runtime-closure checks, candidate commit binding, and explicit native-activation boundaries.
Release validation and candidate lineage
scripts/release-validate.ts, scripts/release-*test.ts, scripts/ship-canary.ts, scripts/ship-canary.test.ts, scripts/distribution-checksums.ts
Validates generated sidecars and checksum identity, then binds source, archive, packaged-payload, and installed-payload hashes during candidate qualification.

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

Sequence Diagram(s)

sequenceDiagram
  participant Candidate
  participant ship-canary
  participant PluginArchive
  participant InstalledClients
  participant HarnessProof
  Candidate->>ship-canary: Qualify exact source commit
  ship-canary->>PluginArchive: Build deterministic archive and payload hash
  ship-canary->>InstalledClients: Install and compare Claude/Codex payloads
  InstalledClients-->>ship-canary: Return installed payload hashes
  ship-canary->>HarnessProof: Validate candidate lineage and capability evidence
  HarnessProof-->>Candidate: Return bound qualification evidence
Loading

Possibly related PRs

Suggested reviewers: nathanvale

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.37% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: a native plugin capability tour with lifecycle hook proof.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/native-plugin-capability-tour

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

@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: 9

🧹 Nitpick comments (15)
plugin/skills/capability-tour/SKILL.md (1)

42-42: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Derive the skill list from the observed catalog.

Line 12 instructs the model to read the installed skill catalog. Line 42 then hardcodes five skill names. A template consumer who renames or removes skills through init gets a response that contradicts the catalog it just read. State the expected default set as an example, and require the reported list to come from the catalog.

♻️ Proposed wording change
-3. `Available portable skills`: list `hello-world`, `skill-a`, `skill-b`, `runtime-custody`, and `capability-tour`, marking the first three Bun-backed and the last two model-only.
+3. `Available portable skills`: list the skills found in the installed catalog, marking each Bun-backed or model-only. The unmodified template ships `hello-world`, `skill-a`, and `skill-b` as Bun-backed plus `runtime-custody` and `capability-tour` as model-only.
🤖 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 `@plugin/skills/capability-tour/SKILL.md` at line 42, Update the “Available
portable skills” instruction to require deriving the reported skills from the
installed catalog read earlier, rather than hardcoding names. Mention
hello-world, skill-a, skill-b, runtime-custody, and capability-tour only as the
expected default set, while preserving the Bun-backed versus model-only
classifications.
scripts/native-capability-fixture.ts (1)

71-75: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Match the write behavior of writeGeneratedFiles.

writeGeneratedFiles in scripts/plugin-config.ts lines 474-483 creates the parent directory and skips the write when the on-disk bytes already match. writeNativeCapabilityFixture does neither.

Two consequences:

  • If plugin/hooks/fixture/ does not exist in the target root, writeFileSync throws ENOENT. scripts/init.ts line 199 writes into an arbitrary initialized root, so the directory is not guaranteed.
  • Every bun run generate rewrites both files and updates their mtimes, even when nothing changed. That defeats mtime-based build caching and makes a no-op generate look like a change.
♻️ Proposed change
-import { existsSync, readFileSync, writeFileSync } from "node:fs"
-import { join } from "node:path"
+import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
+import { dirname, join } from "node:path"
 export function writeNativeCapabilityFixture(root: string): GeneratedFile[] {
 	const files = renderNativeCapabilityFixture(root)
-	for (const file of files) writeFileSync(join(root, file.path), file.contents)
+	for (const file of files) {
+		const path = join(root, file.path)
+		if (existsSync(path) && readFileSync(path, "utf8") === file.contents) continue
+		mkdirSync(dirname(path), { recursive: true })
+		writeFileSync(path, file.contents)
+	}
 	return files
 }
🤖 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/native-capability-fixture.ts` around lines 71 - 75, Update
writeNativeCapabilityFixture to match writeGeneratedFiles: create each generated
file’s parent directory before writing, and compare existing on-disk bytes so
writeFileSync is skipped when contents are unchanged. Preserve returning the
full GeneratedFile[] while ensuring new or changed files are written correctly.
plugin/skills/capability-tour/references/capability-reviewer.md (1)

38-38: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Give mutation more than one legal value.

The schema fixes mutation to the literal "none". A reviewer that cannot confirm the no-mutation constraint has no way to say so, and the caller cannot distinguish a verified claim from a copied constant. SKILL.md line 30 treats the no-mutation report as a precondition for the native-subagent-via-skill label, so the field should be able to fail.

♻️ Proposed schema change
-  "mutation": "none",
+  "mutation": "none | unknown",
🤖 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 `@plugin/skills/capability-tour/references/capability-reviewer.md` at line 38,
Update the mutation field in the capability reviewer schema to allow both the
verified no-mutation value and a failure/uncertain value, instead of restricting
it to the literal "none". Preserve the existing "none" value and align the
additional legal value with the report semantics used by SKILL.md and the
native-subagent-via-skill precondition.
plugin/hooks/native-capability-hook (1)

216-313: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

top_level_stop_guard duplicates about 95 lines of top_level_string.

whitespace, charat, parse_string, the record accumulation block, and the whole structural scan loop are byte-identical between the two awk programs. Only the key comparison and the value parser differ: lines 291-302 read a JSON boolean instead of a string, and line 309 requires matches != 1 instead of tolerating zero matches.

Two copies means every future fix to the scanner must land twice, and any divergence becomes a silent behavior difference between the SessionStart and Stop paths.

Merge the two into one awk program that takes a mode variable.

♻️ Sketch of the consolidation
-top_level_stop_guard() {
-	awk '
-	function whitespace(c) { ... }
-	...
-	' "$1"
-}
+# Scan mode "string" returns a plain top-level string value or __MISSING__.
+# Scan mode "boolean" requires exactly one top-level boolean member.
+top_level_member() {
+	awk -v target="$2" -v mode="$3" "$scanner_program" "$1"
+}

Hold the shared awk source in one shell variable, then branch on mode at the value-parsing step (lines 130-141 versus lines 291-304) and at the final matches check (line 145 versus line 309).

🤖 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 `@plugin/hooks/native-capability-hook` around lines 216 - 313, Consolidate
top_level_stop_guard with top_level_string by moving their identical awk scanner
logic—whitespace, charat, parse_string, input accumulation, and structural
scan—into one shared awk program or shell variable controlled by a mode value.
Branch only at value parsing to handle the existing string versus boolean
contracts, and preserve each path’s distinct final matches validation, including
zero-match tolerance for top_level_string and exactly one match for
top_level_stop_guard.
scripts/native-capability-hook.test.ts (1)

518-562: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

This test writes to tracked repository files.

Lines 541 and 553 overwrite plugin/hooks/fixture/lifecycle-mechanics-proof.generated.json and lifecycle-mechanics-proof.source.json in the working tree. The finally blocks restore them during a normal run. The finally blocks do not run if the test process is killed, for example by a CI job timeout or by Ctrl-C during a watch run. The developer is then left with drifted\r\n in a tracked file, and the next generate:check fails for a reason unrelated to the change.

Copy the repository into a temporary root, then run generate:check there. generate:check resolves its own root from its cwd, so the drift assertions still hold.

♻️ Proposed approach
-function runGenerateCheck(): ReturnType<typeof Bun.spawnSync> {
+function runGenerateCheck(cwd: string = root): ReturnType<typeof Bun.spawnSync> {
 	return Bun.spawnSync({
 		cmd: [process.execPath, "run", "generate:check"],
-		cwd: root,
+		cwd,
 		stdout: "pipe",
 		stderr: "pipe",
 	})
 }

Then create a temporary root with cpSync of plugin.config.json, plugin/, scripts/, package.json, and node_modules, mutate the fixture files inside that root, and drop both try/finally blocks.

🤖 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/native-capability-hook.test.ts` around lines 518 - 562, Update the
test around “generation owns one deterministic LF fixture projection” to operate
on a temporary repository copy instead of tracked files. Create a temporary root
containing plugin.config.json, plugin/, scripts/, package.json, and node_modules
via cpSync, run generate:check with that root as cwd, and mutate the copied
fixture files there; remove the existing try/finally restoration blocks while
preserving the drift and non-canonical-source assertions.
scripts/native-capability-surface.test.ts (2)

13-16: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Parse only the hook declarations instead of every generated file.

Line 15 applies JSON.parse to the contents of every file returned by renderGeneratedFiles. The test then reads two entries. If a future generated file is not JSON, this test fails with a parse error that does not describe the hook contract it verifies. Filter to the hook declaration paths before parsing.

♻️ Proposed refactor
 	const generated = new Map(
-		renderGeneratedFiles(config).map((file) => [file.path, JSON.parse(file.contents)]),
+		renderGeneratedFiles(config)
+			.filter((file) => file.path.startsWith("plugin/hooks/"))
+			.map((file) => [file.path, JSON.parse(file.contents)]),
 	)
🤖 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/native-capability-surface.test.ts` around lines 13 - 16, Update the
setup in the test “generation projects one exact native hook declaration per
supported client” to filter renderGeneratedFiles(config) to the specific hook
declaration paths before calling JSON.parse. Build the generated map only from
those filtered files, preserving the existing assertions for the two hook
declarations.

204-216: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reconsider pinning the exact asset digests.

Lines 206-208 hardcode the SHA-256 of both SVG assets. Any byte change to an asset, including a license header or whitespace fix, fails this test with a digest mismatch that does not describe the intent. The maintainer must then recompute and paste a new constant.

The size bound at line 212 and the branding check at line 214 already express the reviewable intent: the assets stay small and identity-neutral. Deterministic packaged bytes are separately enforced by payloadInventorySha256 and the archive proofs in scripts/prove-distribution.ts. Consider dropping the digest constants, or keep them and add a comment that states how to regenerate them.

Note also that lines 178-188 restate the capabilityHookFiles and capabilityAssetFiles allowlists from scripts/build.ts lines 1628-1635. Importing those constants would keep the test and the validator in step.

🤖 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/native-capability-surface.test.ts` around lines 204 - 216, Remove the
hardcoded SHA-256 expectations and hashing assertions from the “identity-neutral
assets have deterministic packaged bytes” test, retaining the existing size and
branding checks because package determinism is covered elsewhere. Also update
the test’s allowlist setup around capabilityHookFiles and capabilityAssetFiles
to reuse the exported symbols from build.ts instead of duplicating their values,
keeping validation aligned with the build configuration.
scripts/build.ts (1)

1628-1650: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Make the sorted-order requirement of the capability allowlists explicit.

Lines 1702-1709 compare these literals against inventory.filter(...), which pluginPayloadInventory sorts with compareCodeUnits. The comparison therefore requires capabilityHookFiles and capabilityAssetFiles to be declared in compareCodeUnits order. Both lists satisfy that today. A future entry added out of order fails with a contract-mismatch message that does not explain the real cause. Sort the constants at definition, as line 1713 already does for modelOnlySkillFiles.

♻️ Proposed refactor
 const capabilityHookFiles = [
 	"hooks/claude/hooks.json",
 	"hooks/codex/hooks.json",
 	"hooks/fixture/lifecycle-mechanics-proof.generated.json",
 	"hooks/fixture/lifecycle-mechanics-proof.source.json",
 	"hooks/native-capability-hook",
-]
-const capabilityAssetFiles = ["assets/composer-icon.svg", "assets/logo.svg"]
+].sort(compareCodeUnits)
+const capabilityAssetFiles = ["assets/composer-icon.svg", "assets/logo.svg"].sort(compareCodeUnits)
🤖 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/build.ts` around lines 1628 - 1650, Sort capabilityHookFiles and
capabilityAssetFiles at definition using the existing compareCodeUnits ordering,
matching the modelOnlySkillFiles declaration. Preserve the current entries and
make the sorted-order contract explicit so future additions remain aligned with
pluginPayloadInventory output.
scripts/prove-distribution.ts (1)

165-191: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

The payload closure is restated as literals in several files. scripts/build.ts declares the canonical capability allowlists, and the proof script and the surface test each repeat the same skill, hook, and asset lists with their own error messages. Adding or removing one capability file now requires matching edits in every copy, and a missed copy fails late with a message that names the wrong contract. Export the closure constants once from scripts/build.ts and consume them everywhere.

  • scripts/prove-distribution.ts#L165-L191: replace the inline ["capability-tour","hello-world","runtime-custody","skill-a","skill-b"] and ["hello-world","skill-a","skill-b"] literals with the shared exported constants, and reuse them for the hookIndependentSkills list at lines 269-275.
  • scripts/native-capability-surface.test.ts#L176-L216: import capabilityHookFiles and capabilityAssetFiles instead of restating both inventories at lines 178-188 and the sidecar list at lines 219-226.
  • scripts/build.ts#L1628-L1650: export capabilityHookFiles, capabilityAssetFiles, and modelOnlySkillFiles, and add exported constants for the portable and executable skill identifiers that scripts/prove-harness-install.ts also duplicates.
🤖 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/prove-distribution.ts` around lines 165 - 191, Centralize the payload
closure definitions in scripts/build.ts by exporting capabilityHookFiles,
capabilityAssetFiles, modelOnlySkillFiles, and the portable/executable skill
identifier constants used by scripts/prove-harness-install.ts. In
scripts/prove-distribution.ts lines 165-191 and 269-275, replace duplicated
skill and hook-independent lists with those exports; in
scripts/native-capability-surface.test.ts lines 176-216, import and reuse
capabilityHookFiles and capabilityAssetFiles for the inventories and sidecar
list. Keep each existing validation aligned with the shared constants and retain
the relevant contract-specific error messages.
scripts/prove-harness-install.ts (2)

1417-1417: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unnecessary escape in the template literal.

Inside a backtick template literal, \" produces the same " character. The escape suggests the pattern is quoted when it is not.

♻️ Proposed cleanup
-		if (!launcher.includes(`runtime/runtime-exec\" run ${skillId} --`) || launcher.includes("hooks/")) {
+		if (!launcher.includes(`runtime/runtime-exec" run ${skillId} --`) || launcher.includes("hooks/")) {
🤖 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/prove-harness-install.ts` at line 1417, Remove the unnecessary
backslash before the double quote in the template literal used by the launcher
condition near the runtime-exec pattern. Keep the matching text and the existing
hooks/ alternative unchanged.

1500-1503: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider reporting a failed receipt as failed, not unknown.

When nativeQualification.status is "failed", line 1502 sets externalCandidateQualification to "unknown". A failed receipt was ingested, so "unknown" understates what the proof knows. The full receipt is still preserved in nativeQualification, so no evidence is lost, but a reader of externalCandidateQualification alone cannot distinguish "no receipt" from "receipt failed". Adding a "failed" member to the union would make the three states distinct.

This changes the asserted value in scripts/prove-harness-install.test.ts (line 238), so treat it as a deliberate contract decision rather than a defect.

🤖 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/prove-harness-install.ts` around lines 1500 - 1503, Update
externalCandidateQualification in the proof summary to report "failed" when
nativeQualification.status is "failed", while retaining "proved" for successful
native qualification and "unknown" when no qualification result exists. Extend
the associated status union and update the corresponding assertion in the prove
harness tests to reflect this deliberate three-state contract.
scripts/ship-canary.ts (1)

168-175: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

The error message blames the checksum when expectedSourceCommit is malformed.

Line 168 combines two distinct failures: an invalid expectedSourceCommit format, and a checksum that does not match it. Both report qualification checksum source commit does not match the exact candidate. If the caller passes a malformed expected commit, the message points the operator at the checksum instead of the caller. Splitting the format check would make the cause clear.

🤖 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/ship-canary.ts` around lines 168 - 175, Split the combined guard
around expectedSourceCommit into separate validation paths: when it fails the
40-character lowercase hexadecimal format check, throw a CanaryError identifying
the expected commit as malformed; otherwise retain the existing
checksum-mismatch error only when checksums.sourceCommit differs from
expectedSourceCommit.
scripts/prove-harness-install.test.ts (1)

377-401: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the specific rejection reason for each tampered path.

.toThrow() with no argument passes for any thrown error. Each of the four paths trips a different guard in proveInstalledCapabilityEvidence: hooks/claude/hooks.json fails the declaration-bytes check, hooks/fixture/lifecycle-mechanics-proof.generated.json fails the fixture-equality check, and the other two reach the payload-hash check. If a future refactor removes one guard, the test still passes because a different guard throws.

Assert the expected message per case.

♻️ Proposed refinement
 test.each([
-	"assets/logo.svg",
-	"skills/capability-tour/references/capability-reviewer.md",
-	"hooks/claude/hooks.json",
-	"hooks/fixture/lifecycle-mechanics-proof.generated.json",
-] as const)("installed capability evidence rejects tampered %s bytes", (path) => {
+	["assets/logo.svg", "installed payload hash differs"],
+	["skills/capability-tour/references/capability-reviewer.md", "installed payload hash differs"],
+	["hooks/claude/hooks.json", "installed declaration bytes do not match"],
+	["hooks/fixture/lifecycle-mechanics-proof.generated.json", "lifecycle mechanics proof fixture differs"],
+] as const)("installed capability evidence rejects tampered %s bytes", (path, reason) => {
 	const temporaryRoot = mkdtempSync(join(tmpdir(), "installed-capability-tamper-"))
 	const pluginRoot = join(temporaryRoot, "plugin")
 	try {
 		cpSync(join(root, "plugin"), pluginRoot, { recursive: true })
 		const candidatePayloadHash = runtimeClosureEvidence(pluginRoot).payloadHash
 		const absolutePath = join(pluginRoot, path)
 		writeFileSync(absolutePath, Buffer.concat([readFileSync(absolutePath), Buffer.from("tampered\n")]))
 		expect(() =>
 			proveInstalledCapabilityEvidence(
 				pluginRoot,
 				"claude",
 				"a".repeat(40),
 				candidatePayloadHash,
 			),
-		).toThrow()
+		).toThrow(reason)
 	} finally {
🤖 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/prove-harness-install.test.ts` around lines 377 - 401, Update the
parameterized test for proveInstalledCapabilityEvidence to associate each
tampered path with its expected rejection message, then pass that message to
toThrow for path-specific assertions. Expect declaration-bytes rejection for
hooks/claude/hooks.json, fixture-equality rejection for
hooks/fixture/lifecycle-mechanics-proof.generated.json, and payload-hash
rejection for assets/logo.svg and
skills/capability-tour/references/capability-reviewer.md.
scripts/harness-install-codex.ts (1)

167-169: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider deriving the Codex hook path from one exported constant.

The literal "./hooks/codex/hooks.json" now appears here, in scripts/prove-dx.ts (line 45), in scripts/release-validate.ts (line 523), and is derived in scripts/prove-harness-install.ts (line 1368). scripts/plugin-config.ts generates the same path in hookDeclaration. If the layout changes, four sites must change together. Export one constant (or a hookDeclarationPath(client) helper) from scripts/plugin-config.ts and reuse it in the non-proof sites.

🤖 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/harness-install-codex.ts` around lines 167 - 169, Centralize the
Codex hook declaration path in scripts/plugin-config.ts by exporting one shared
constant or a hookDeclarationPath(client) helper, then replace the duplicated
literal and equivalent derivation in scripts/harness-install-codex.ts,
scripts/prove-dx.ts, scripts/release-validate.ts, and other non-proof consumers
with that shared symbol. Preserve the existing generated hookDeclaration
behavior and exact path value.
scripts/release-validate.ts (1)

27-29: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Report every drifted sidecar, not only the first.

Line 23-26 collects all drifted paths, then line 28 reports drifted[0]. A maintainer fixing drift must rerun validation once per file. List all of them.

♻️ Proposed cleanup
 	if (drifted.length > 0) {
-		throw new Error(`generated capability sidecar differs from its source: ${drifted[0]}`)
+		throw new Error(`generated capability sidecars differ from their sources: ${drifted.join(", ")}`)
 	}
🤖 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-validate.ts` around lines 27 - 29, Update the drift
validation check to report every path collected in drifted rather than only
drifted[0]. Preserve the existing error behavior while joining or formatting the
complete list in the Error message.
🤖 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 @.claude-plugin/marketplace.json:
- Line 13: Update claudeMarketplace() to include displayName:
config.displayName, regenerate or edit .claude-plugin/marketplace.json so the
marketplace entry exposes that value, and extend
scripts/plugin-manifest-contract.test.ts to assert the displayName field.

In `@CONTEXT.md`:
- Around line 43-44: Update the Capability Tour description to say it reports
external qualification status, not qualification receipts. Explicitly state that
it never reads, ingests, or infers private qualification receipts, while
preserving the qualification boundary outside the tour and clarifying that its
default result is not an automated native-qualification claim.

In `@docs/plans/native-plugin-capability-tour-plan.md`:
- Around line 243-247: Update the plan’s fail-open behavior to explicitly
require bounded warnings on stderr while keeping stdout empty. Extend
malformed-input, oversized-input, and operational Stop-failure test coverage to
both claude and codex clients, not just claude.

In `@plugin/hooks/native-capability-hook`:
- Around line 52-59: Update both scanner functions containing charat() to detect
the non-portable one-field split fallback: when char_indexed == 0 and
document_length > 65536, exit immediately and fail open before scanning.
Preserve the existing indexed-character path and behavior for documents at or
below the threshold.

In `@scripts/native-capability-hook.test.ts`:
- Around line 418-427: Guard the unreadable-file scenario in the test using a
root-process check, and build the hook-case list so unreadable is omitted when
running as root: [missing, ...(runningAsRoot ? [] : [unreadable]),
noComparison]. Keep the existing chmod setup and assertions unchanged for
non-root environments.
- Line 19: Update the root path initialization near the root constant to use
fileURLToPath on the URL derived from import.meta.url instead of reading
URL.pathname, while preserving the trailing-slash removal so subsequent
join(root, ...) calls receive a decoded filesystem path.

In `@scripts/prove-harness-install.ts`:
- Around line 1700-1706: Update the configuredSourceCommit selection near
resolveCleanSourceCommit and resolveCandidatePayloadCommit to treat empty
SOURCE_COMMIT values as unset, then fall back to GITHUB_SHA or undefined.
Preserve the mismatch validation for non-empty commit values so only an actual
configured commit is compared with sourceCommit.

In `@scripts/release-validate.ts`:
- Around line 11-36: Update validateCapabilitySidecars to verify every path in
STATIC_CAPABILITY_SIDECAR_PATHS exists under repositoryRoot before returning the
inventory, using existsSync and preserving the existing generated-file drift
checks. Throw a validation error identifying the missing sidecar instead of
admitting it as present.

In `@scripts/ship-canary.ts`:
- Around line 1008-1036: Constrain manifestName in installCandidate to the same
kebab-case format enforced by the existing plugin configuration validation
before interpolating it into the deterministicPluginArchive package name. Reject
empty or invalid names with the existing install_mismatch CanaryError, and only
construct the archive path after validation.

---

Nitpick comments:
In `@plugin/hooks/native-capability-hook`:
- Around line 216-313: Consolidate top_level_stop_guard with top_level_string by
moving their identical awk scanner logic—whitespace, charat, parse_string, input
accumulation, and structural scan—into one shared awk program or shell variable
controlled by a mode value. Branch only at value parsing to handle the existing
string versus boolean contracts, and preserve each path’s distinct final matches
validation, including zero-match tolerance for top_level_string and exactly one
match for top_level_stop_guard.

In `@plugin/skills/capability-tour/references/capability-reviewer.md`:
- Line 38: Update the mutation field in the capability reviewer schema to allow
both the verified no-mutation value and a failure/uncertain value, instead of
restricting it to the literal "none". Preserve the existing "none" value and
align the additional legal value with the report semantics used by SKILL.md and
the native-subagent-via-skill precondition.

In `@plugin/skills/capability-tour/SKILL.md`:
- Line 42: Update the “Available portable skills” instruction to require
deriving the reported skills from the installed catalog read earlier, rather
than hardcoding names. Mention hello-world, skill-a, skill-b, runtime-custody,
and capability-tour only as the expected default set, while preserving the
Bun-backed versus model-only classifications.

In `@scripts/build.ts`:
- Around line 1628-1650: Sort capabilityHookFiles and capabilityAssetFiles at
definition using the existing compareCodeUnits ordering, matching the
modelOnlySkillFiles declaration. Preserve the current entries and make the
sorted-order contract explicit so future additions remain aligned with
pluginPayloadInventory output.

In `@scripts/harness-install-codex.ts`:
- Around line 167-169: Centralize the Codex hook declaration path in
scripts/plugin-config.ts by exporting one shared constant or a
hookDeclarationPath(client) helper, then replace the duplicated literal and
equivalent derivation in scripts/harness-install-codex.ts, scripts/prove-dx.ts,
scripts/release-validate.ts, and other non-proof consumers with that shared
symbol. Preserve the existing generated hookDeclaration behavior and exact path
value.

In `@scripts/native-capability-fixture.ts`:
- Around line 71-75: Update writeNativeCapabilityFixture to match
writeGeneratedFiles: create each generated file’s parent directory before
writing, and compare existing on-disk bytes so writeFileSync is skipped when
contents are unchanged. Preserve returning the full GeneratedFile[] while
ensuring new or changed files are written correctly.

In `@scripts/native-capability-hook.test.ts`:
- Around line 518-562: Update the test around “generation owns one deterministic
LF fixture projection” to operate on a temporary repository copy instead of
tracked files. Create a temporary root containing plugin.config.json, plugin/,
scripts/, package.json, and node_modules via cpSync, run generate:check with
that root as cwd, and mutate the copied fixture files there; remove the existing
try/finally restoration blocks while preserving the drift and
non-canonical-source assertions.

In `@scripts/native-capability-surface.test.ts`:
- Around line 13-16: Update the setup in the test “generation projects one exact
native hook declaration per supported client” to filter
renderGeneratedFiles(config) to the specific hook declaration paths before
calling JSON.parse. Build the generated map only from those filtered files,
preserving the existing assertions for the two hook declarations.
- Around line 204-216: Remove the hardcoded SHA-256 expectations and hashing
assertions from the “identity-neutral assets have deterministic packaged bytes”
test, retaining the existing size and branding checks because package
determinism is covered elsewhere. Also update the test’s allowlist setup around
capabilityHookFiles and capabilityAssetFiles to reuse the exported symbols from
build.ts instead of duplicating their values, keeping validation aligned with
the build configuration.

In `@scripts/prove-distribution.ts`:
- Around line 165-191: Centralize the payload closure definitions in
scripts/build.ts by exporting capabilityHookFiles, capabilityAssetFiles,
modelOnlySkillFiles, and the portable/executable skill identifier constants used
by scripts/prove-harness-install.ts. In scripts/prove-distribution.ts lines
165-191 and 269-275, replace duplicated skill and hook-independent lists with
those exports; in scripts/native-capability-surface.test.ts lines 176-216,
import and reuse capabilityHookFiles and capabilityAssetFiles for the
inventories and sidecar list. Keep each existing validation aligned with the
shared constants and retain the relevant contract-specific error messages.

In `@scripts/prove-harness-install.test.ts`:
- Around line 377-401: Update the parameterized test for
proveInstalledCapabilityEvidence to associate each tampered path with its
expected rejection message, then pass that message to toThrow for path-specific
assertions. Expect declaration-bytes rejection for hooks/claude/hooks.json,
fixture-equality rejection for
hooks/fixture/lifecycle-mechanics-proof.generated.json, and payload-hash
rejection for assets/logo.svg and
skills/capability-tour/references/capability-reviewer.md.

In `@scripts/prove-harness-install.ts`:
- Line 1417: Remove the unnecessary backslash before the double quote in the
template literal used by the launcher condition near the runtime-exec pattern.
Keep the matching text and the existing hooks/ alternative unchanged.
- Around line 1500-1503: Update externalCandidateQualification in the proof
summary to report "failed" when nativeQualification.status is "failed", while
retaining "proved" for successful native qualification and "unknown" when no
qualification result exists. Extend the associated status union and update the
corresponding assertion in the prove harness tests to reflect this deliberate
three-state contract.

In `@scripts/release-validate.ts`:
- Around line 27-29: Update the drift validation check to report every path
collected in drifted rather than only drifted[0]. Preserve the existing error
behavior while joining or formatting the complete list in the Error message.

In `@scripts/ship-canary.ts`:
- Around line 168-175: Split the combined guard around expectedSourceCommit into
separate validation paths: when it fails the 40-character lowercase hexadecimal
format check, throw a CanaryError identifying the expected commit as malformed;
otherwise retain the existing checksum-mismatch error only when
checksums.sourceCommit differs from expectedSourceCommit.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d7f230ce-ef32-467a-b41b-dacbb0d6e1e7

📥 Commits

Reviewing files that changed from the base of the PR and between db4d5ff and d649319.

⛔ Files ignored due to path filters (3)
  • plugin/assets/composer-icon.svg is excluded by !**/*.svg
  • plugin/assets/logo.svg is excluded by !**/*.svg
  • plugin/hooks/fixture/lifecycle-mechanics-proof.generated.json is excluded by !**/*.generated.*
📒 Files selected for processing (43)
  • .claude-plugin/marketplace.json
  • CONTEXT.md
  • README.md
  • docs/adr/0005-shared-runtime-custody.md
  • docs/adr/0008-native-plugin-capability-tour.md
  • docs/plans/native-plugin-capability-tour-plan.md
  • plugin.config.json
  • plugin/.claude-plugin/plugin.json
  • plugin/.codex-plugin/plugin.json
  • plugin/hooks/claude/hooks.json
  • plugin/hooks/codex/hooks.json
  • plugin/hooks/fixture/lifecycle-mechanics-proof.source.json
  • plugin/hooks/native-capability-hook
  • plugin/skills/capability-tour/SKILL.md
  • plugin/skills/capability-tour/references/capability-reviewer.md
  • scripts/build.test.ts
  • scripts/build.ts
  • scripts/dev.test.ts
  • scripts/dev.ts
  • scripts/distribution-checksums.test.ts
  • scripts/distribution-checksums.ts
  • scripts/generate.ts
  • scripts/harness-install-codex.test.ts
  • scripts/harness-install-codex.ts
  • scripts/init.test.ts
  • scripts/init.ts
  • scripts/native-capability-fixture.ts
  • scripts/native-capability-hook.test.ts
  • scripts/native-capability-surface.test.ts
  • scripts/package.ts
  • scripts/plugin-config.ts
  • scripts/plugin-files.ts
  • scripts/plugin-manifest-contract.test.ts
  • scripts/prove-distribution.ts
  • scripts/prove-dx.ts
  • scripts/prove-harness-install.test.ts
  • scripts/prove-harness-install.ts
  • scripts/release-projection.test.ts
  • scripts/release-validate.test.ts
  • scripts/release-validate.ts
  • scripts/runtime-custody-generation.test.ts
  • scripts/ship-canary.test.ts
  • scripts/ship-canary.ts

Comment thread .claude-plugin/marketplace.json
Comment thread CONTEXT.md Outdated
Comment thread docs/plans/native-plugin-capability-tour-plan.md Outdated
Comment thread plugin/hooks/native-capability-hook Outdated
Comment thread scripts/native-capability-hook.test.ts Outdated
Comment thread scripts/native-capability-hook.test.ts Outdated
Comment thread scripts/prove-harness-install.ts
Comment thread scripts/release-validate.ts
Comment thread scripts/ship-canary.ts
@nathanvale

Copy link
Copy Markdown
Contributor

@codex full review

@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: d649319597

ℹ️ 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 plugin/hooks/native-capability-hook Outdated
@myagentdojo
myagentdojo temporarily deployed to hosted-canary-qualification August 10, 2026 01:17 — with GitHub Actions Inactive
@myagentdojo

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 10, 2026 •

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@myagentdojo

Copy link
Copy Markdown
Owner Author

@codex review

@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: 74675e6d7f

ℹ️ 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 plugin/skills/capability-tour/SKILL.md Outdated
Comment thread plugin/hooks/native-capability-hook Outdated

@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: 5

🧹 Nitpick comments (2)
scripts/native-capability-surface.test.ts (1)

176-188: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Import the capability sidecar lists instead of restating them.

scripts/build.ts defines capabilityHookFiles and capabilityAssetFiles for the same contract (Lines 1628-1635). This test restates both lists literally, so the contract now has two sources of truth. Export the constants from scripts/build.ts and assert against them here. The test then still fails when a sidecar is added or removed without updating the payload closure, but it fails for one reason instead of two.

🤖 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/native-capability-surface.test.ts` around lines 176 - 188, Export the
existing capabilityHookFiles and capabilityAssetFiles constants from
scripts/build.ts, then import and use them in the payload inventory test instead
of duplicating the literal sidecar lists. Preserve the existing filtering and
assertions so the test continues validating the payload closure against the
shared contract.
scripts/prove-harness-install.test.ts (1)

377-401: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the specific rejection reason for each tampered path.

.toThrow() with no argument accepts any error. A future refactor that makes proveInstalledCapabilityEvidence fail earlier for an unrelated reason, for example a missing file or a JSON parse error, would keep this test green while the tamper detection is gone. The neighbouring tests at lines 417 and 439 already assert exact messages.

Bind each case to its expected failure.

♻️ Proposed refactor
 test.each([
-	"assets/logo.svg",
-	"skills/capability-tour/references/capability-reviewer.md",
-	"hooks/claude/hooks.json",
-	"hooks/fixture/lifecycle-mechanics-proof.generated.json",
-] as const)("installed capability evidence rejects tampered %s bytes", (path) => {
+	["assets/logo.svg", "installed payload hash differs"],
+	["skills/capability-tour/references/capability-reviewer.md", "installed payload hash differs"],
+	["hooks/claude/hooks.json", "installed declaration bytes do not match"],
+	["hooks/fixture/lifecycle-mechanics-proof.generated.json", "lifecycle mechanics proof fixture differs"],
+] as const)("installed capability evidence rejects tampered %s bytes", (path, reason) => {
 	const temporaryRoot = mkdtempSync(join(tmpdir(), "installed-capability-tamper-"))
 	const pluginRoot = join(temporaryRoot, "plugin")
 	try {
 		cpSync(join(root, "plugin"), pluginRoot, { recursive: true })
 		const candidatePayloadHash = runtimeClosureEvidence(pluginRoot).payloadHash
 		const absolutePath = join(pluginRoot, path)
 		writeFileSync(absolutePath, Buffer.concat([readFileSync(absolutePath), Buffer.from("tampered\n")]))
 		expect(() =>
 			proveInstalledCapabilityEvidence(
 				pluginRoot,
 				"claude",
 				"a".repeat(40),
 				candidatePayloadHash,
 			),
-		).toThrow()
+		).toThrow(reason)
 	} finally {
 		rmSync(temporaryRoot, { recursive: true, force: true })
 	}
 })
🤖 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/prove-harness-install.test.ts` around lines 377 - 401, Update the
parameterized tampering test around proveInstalledCapabilityEvidence to include
the expected rejection message for each path, and pass that message to toThrow.
Bind each tampered file case to its specific failure reason, matching the
exact-message assertions used by the neighboring tests, so the test verifies
tamper detection rather than merely any thrown error.
🤖 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 `@plugin/hooks/codex/hooks.json`:
- Line 8: Update the Codex hook path generation in plugin-config.ts to use the
provided PLUGIN_ROOT variable instead of CODEX_PLUGIN_ROOT, regenerate both
Codex hook commands in hooks.json, and update the generated-manifest assertion
to expect the new variable.

In `@plugin/skills/capability-tour/SKILL.md`:
- Line 8: Update the installed plugin root resolution in the capability-tour
instructions to use three directories above SKILL.md, equivalent to ../../.., so
it resolves plugin rather than plugin/skills and can locate .claude-plugin,
.codex-plugin, and hooks.

In `@scripts/build.ts`:
- Around line 1628-1634: Update the executable-mode validation around the
capability hook file list and payload checks to include
hooks/native-capability-hook alongside runtime/runtime-exec and bin launchers.
Ensure packaging or release validation fails when this hook lacks any executable
bit, preserving the source mode requirement.

In `@scripts/prove-distribution.ts`:
- Around line 225-229: Update the configured commit selection near
resolveCandidatePayloadCommit to use the same empty-tolerant fallback pattern as
prove-harness-install.ts, so an empty SOURCE_COMMIT falls through to GITHUB_SHA
and does not trigger the mismatch check. Preserve the existing comparison and
error behavior for non-empty configured commits.

In `@scripts/prove-harness-install.ts`:
- Around line 1434-1443: Add a 15,000 ms timeout to the Bun.spawnSync options in
runHandler, applying it to both SessionStart and Stop hook invocations while
preserving the existing command, environment, and stream configuration.

---

Nitpick comments:
In `@scripts/native-capability-surface.test.ts`:
- Around line 176-188: Export the existing capabilityHookFiles and
capabilityAssetFiles constants from scripts/build.ts, then import and use them
in the payload inventory test instead of duplicating the literal sidecar lists.
Preserve the existing filtering and assertions so the test continues validating
the payload closure against the shared contract.

In `@scripts/prove-harness-install.test.ts`:
- Around line 377-401: Update the parameterized tampering test around
proveInstalledCapabilityEvidence to include the expected rejection message for
each path, and pass that message to toThrow. Bind each tampered file case to its
specific failure reason, matching the exact-message assertions used by the
neighboring tests, so the test verifies tamper detection rather than merely any
thrown error.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2e83dc71-134c-4929-9755-94e744e6a228

📥 Commits

Reviewing files that changed from the base of the PR and between db4d5ff and 74675e6.

⛔ Files ignored due to path filters (3)
  • plugin/assets/composer-icon.svg is excluded by !**/*.svg
  • plugin/assets/logo.svg is excluded by !**/*.svg
  • plugin/hooks/fixture/lifecycle-mechanics-proof.generated.json is excluded by !**/*.generated.*
📒 Files selected for processing (43)
  • .claude-plugin/marketplace.json
  • CONTEXT.md
  • README.md
  • docs/adr/0005-shared-runtime-custody.md
  • docs/adr/0008-native-plugin-capability-tour.md
  • docs/plans/native-plugin-capability-tour-plan.md
  • plugin.config.json
  • plugin/.claude-plugin/plugin.json
  • plugin/.codex-plugin/plugin.json
  • plugin/hooks/claude/hooks.json
  • plugin/hooks/codex/hooks.json
  • plugin/hooks/fixture/lifecycle-mechanics-proof.source.json
  • plugin/hooks/native-capability-hook
  • plugin/skills/capability-tour/SKILL.md
  • plugin/skills/capability-tour/references/capability-reviewer.md
  • scripts/build.test.ts
  • scripts/build.ts
  • scripts/dev.test.ts
  • scripts/dev.ts
  • scripts/distribution-checksums.test.ts
  • scripts/distribution-checksums.ts
  • scripts/generate.ts
  • scripts/harness-install-codex.test.ts
  • scripts/harness-install-codex.ts
  • scripts/init.test.ts
  • scripts/init.ts
  • scripts/native-capability-fixture.ts
  • scripts/native-capability-hook.test.ts
  • scripts/native-capability-surface.test.ts
  • scripts/package.ts
  • scripts/plugin-config.ts
  • scripts/plugin-files.ts
  • scripts/plugin-manifest-contract.test.ts
  • scripts/prove-distribution.ts
  • scripts/prove-dx.ts
  • scripts/prove-harness-install.test.ts
  • scripts/prove-harness-install.ts
  • scripts/release-projection.test.ts
  • scripts/release-validate.test.ts
  • scripts/release-validate.ts
  • scripts/runtime-custody-generation.test.ts
  • scripts/ship-canary.test.ts
  • scripts/ship-canary.ts

Comment thread plugin/hooks/codex/hooks.json Outdated
Comment thread plugin/skills/capability-tour/SKILL.md Outdated
Comment thread scripts/build.ts Outdated
Comment thread scripts/prove-distribution.ts
Comment thread scripts/prove-harness-install.ts
@myagentdojo
myagentdojo temporarily deployed to hosted-canary-qualification August 10, 2026 01:40 — with GitHub Actions Inactive
@myagentdojo

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@myagentdojo

Copy link
Copy Markdown
Owner Author

@codex review

@coderabbitai

coderabbitai Bot commented Aug 10, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 31 minutes.

@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: e2475755ef

ℹ️ 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 plugin/hooks/native-capability-hook
@myagentdojo
myagentdojo temporarily deployed to hosted-canary-qualification August 10, 2026 02:00 — with GitHub Actions Inactive
@myagentdojo

Copy link
Copy Markdown
Owner Author

@codex review

@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: 0f9fe7a34e

ℹ️ 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/native-capability-hook.test.ts

@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: 7

🧹 Nitpick comments (15)
scripts/native-capability-fixture.ts (1)

71-75: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Two copies of the fixture write loop skip the directory guard used by writeGeneratedFiles. writeGeneratedFiles in scripts/plugin-config.ts lines 475-484 creates the parent directory and skips a file whose bytes already match. Both fixture write sites do neither, so a root without plugin/hooks/fixture/ throws ENOENT, and unchanged files are rewritten on every run.

  • scripts/native-capability-fixture.ts#L71-L75: add mkdirSync(dirname(path), { recursive: true }) and the identical-content skip inside writeNativeCapabilityFixture.
  • scripts/init.ts#L194-L199: remove the duplicated for (const file of fixtureFiles) writeFileSync(...) loop and call the shared writer instead, so the guard applies in both paths.
🤖 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/native-capability-fixture.ts` around lines 71 - 75, Update
scripts/native-capability-fixture.ts:71-75 in writeNativeCapabilityFixture to
create each file’s parent directory recursively and skip writing when existing
contents match. Update scripts/init.ts:194-199 by removing the duplicated
fixture write loop and using the shared writer so both paths apply the same
guards.
scripts/native-capability-hook.test.ts (1)

533-577: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

This test mutates tracked repository files.

Lines 556 and 568 overwrite plugin/hooks/fixture/lifecycle-mechanics-proof.generated.json and ...source.json inside the working tree. The finally blocks restore them only on a normal completion. If the runner is interrupted, or an expect throws in a way that aborts the process, the checked-in fixture stays drifted and every later generate:check fails.

Every other test in this file works inside mkdtempSync roots. This test does not.

Copy plugin/ and plugin.config.json into a temporary root, then run generate:check with cwd set to that root. The drift assertions then stay isolated.

🤖 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/native-capability-hook.test.ts` around lines 533 - 577, Update the
test generation fixture setup around “generation owns one deterministic LF
fixture projection” to create an isolated temporary root with mkdtempSync, copy
plugin/ and plugin.config.json into it, and point all source/projection paths
and runGenerateCheck calls at that root via cwd. Keep the existing
canonical-content and drift assertions, but ensure no tracked repository files
are mutated.
plugin/hooks/native-capability-hook (1)

262-267: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Use substr() with index() for the + separator

Common awk implementations treat a single-character separator literally, but POSIX specifies split() separators as EREs and does not require this behavior. Use substr() with index() or an escaped ERE such as /\+/ to avoid rejecting valid versions such as 1.2.3+build.7.

🤖 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 `@plugin/hooks/native-capability-hook` around lines 262 - 267, Update the
+-separator parsing in the identifiers validation block to avoid relying on
split(main, build_parts, "+") treating the separator literally; use index() with
substr() (or an explicitly escaped ERE) to extract the main version and build
metadata, while preserving the existing identifiers validation and rejection
behavior.
scripts/native-capability-surface.test.ts (2)

112-117: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Inline the single-element loop.

for (const field of ["hooks"] as const) iterates one value. The outer loop adds indirection without adding coverage.

♻️ Proposed refactor
-	for (const field of ["hooks"] as const) {
-		for (const manifest of [claudeManifest, codexManifest]) {
-			expect(manifest[field].startsWith("./")).toBe(true)
-			expect(existsSync(join(root, "plugin", manifest[field]))).toBe(true)
-		}
-	}
+	for (const manifest of [claudeManifest, codexManifest]) {
+		expect(manifest.hooks.startsWith("./")).toBe(true)
+		expect(existsSync(join(root, "plugin", manifest.hooks))).toBe(true)
+	}
🤖 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/native-capability-surface.test.ts` around lines 112 - 117, Remove the
single-element outer loop in the test and reference the "hooks" field directly
when validating claudeManifest and codexManifest. Preserve both existing
assertions for the hooks path and file existence checks.

234-264: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Documentation prose assertions will fail on benign rewrites.

This test requires exact substrings in README.md, CONTEXT.md, and two ADRs, including "0700", "0600", and "macOS and Linux POSIX hosts". Any editorial change to those documents breaks the build even when the underlying behavior is unchanged. If the intent is to prevent overclaiming, keep the negative assertions and the exclusion checks, and reduce the positive prose list to the claims that map to enforced code behavior.

🤖 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/native-capability-surface.test.ts` around lines 234 - 264, Update the
test named “repository docs separate lifecycle mechanics from fresh-native
qualification” to remove brittle positive assertions for editorial prose,
especially exact wording such as “0700”, “0600”, and “macOS and Linux POSIX
hosts”. Retain the negative overclaim checks and capability exclusion
assertions, and keep only positive claims directly tied to enforced behavior.
scripts/package.ts (1)

72-100: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

Derive archiveBytes from the same buffer that produced the digest.

archiveDigest comes from archiveArtifact.bytes, but archiveBytes re-reads the size from disk. The two values then describe the file through different paths. Use the buffer length so the size and digest always describe the same bytes.

♻️ Proposed refactor
-const archiveBytes = statSync(archive).size
+const archiveBytes = archiveArtifact.bytes.byteLength

Remove statSync from the node:fs import if it becomes unused.

🤖 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/package.ts` around lines 72 - 100, Update the archive metadata flow
around archiveArtifact and archiveDigest to derive archiveBytes directly from
archiveArtifact.bytes.length instead of statSync(archive).size, ensuring the
reported size and digest describe the same buffer. Remove statSync from the
node:fs import if it is no longer used.
scripts/runtime-custody-generation.test.ts (1)

93-103: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Import readdirSync statically.

The dynamic (await import("node:fs")) appears at Line 93 and again at Line 133. A static import is clearer and removes the repeated module resolution.

♻️ Proposed refactor

Add the static import at the top of the file:

import { readdirSync } from "node:fs"

Then simplify both call sites:

-	const installedSkills = (await import("node:fs")).readdirSync(
-		fileURLToPath(new URL("../plugin/skills", import.meta.url)),
-	).sort()
+	const installedSkills = readdirSync(
+		fileURLToPath(new URL("../plugin/skills", import.meta.url)),
+	).sort()
🤖 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/runtime-custody-generation.test.ts` around lines 93 - 103, Replace
the repeated dynamic node:fs imports in the test with a single top-level static
readdirSync import, then update both call sites in the relevant test flows to
call readdirSync directly while preserving their existing arguments and
assertions.
scripts/plugin-files.ts (1)

223-234: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Include the child stderr in the failure messages.

tar and gzip inherit stderr, so the diagnostic text goes to the parent stream while the thrown Error carries only the exit code. In CI the two streams are often separated, which makes packaging failures hard to attribute. Capture stderr and attach it to the error.

♻️ Proposed refactor
-		const tar = Bun.spawnSync({ cmd: tarArguments, stdout: "inherit", stderr: "inherit" })
+		const tar = Bun.spawnSync({ cmd: tarArguments, stdout: "inherit", stderr: "pipe" })
 		if (tar.exitCode !== 0) {
-			throw new Error(`deterministic archive tar failed with exit code ${tar.exitCode}`)
+			throw new Error(
+				`deterministic archive tar failed with exit code ${tar.exitCode}: ${tar.stderr.toString()}`,
+			)
 		}
 		const gzip = Bun.spawnSync({
 			cmd: ["gzip", "-n", "-9", "-c", uncompressedArchive],
 			stdout: "pipe",
-			stderr: "inherit",
+			stderr: "pipe",
 		})
 		if (gzip.exitCode !== 0) {
-			throw new Error(`deterministic archive gzip failed with exit code ${gzip.exitCode}`)
+			throw new Error(
+				`deterministic archive gzip failed with exit code ${gzip.exitCode}: ${gzip.stderr.toString()}`,
+			)
 		}
🤖 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/plugin-files.ts` around lines 223 - 234, Update the tar and gzip
Bun.spawnSync calls in the deterministic archive flow to capture child stderr
instead of inheriting it, then include the captured stderr text in each thrown
failure Error alongside the exit code. Preserve stdout behavior and ensure both
failure messages identify the corresponding command.
scripts/release-projection.test.ts (1)

36-56: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

The isReleasePleaseProjection: false assertion is satisfied by the missing identity.

classifyReleaseImpact sets isReleasePleaseProjection to false whenever pullRequestIdentity is absent, and this call omits it. The assertion therefore passes without exercising the projection path. To prove that a capability sidecar cannot be admitted as a release-please projection, supply a matching pullRequestIdentity with expectedAutomationLogin, authorLogin, and a release-please--branches-- head ref, and keep the same expectation.

🤖 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-projection.test.ts` around lines 36 - 56, Update the
classifyReleaseImpact call in the static capability sidecars test to include a
matching pullRequestIdentity with expectedAutomationLogin, authorLogin, and a
release-please--branches-- head ref. Keep the existing payloadChanged,
isReleasePleaseProjection, and ok expectations so the assertion exercises the
projection path rather than the missing-identity fallback.
scripts/build.ts (1)

1628-1650: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Sort the hook and asset allowlists at comparison time.

capabilityHookFiles and capabilityAssetFiles are compared against the sorted inventory without sorting (Lines 1702-1709), so the literal order must match compareCodeUnits exactly. The skill comparison already sorts (Line 1713). If a maintainer adds an entry in the wrong position, validation fails with an inventory-mismatch error that does not point at the authoring mistake. Sort both lists once at module scope.

♻️ Proposed refactor
-const capabilityHookFiles = [
+const capabilityHookFiles = [
 	"hooks/claude/hooks.json",
 	"hooks/codex/hooks.json",
 	"hooks/fixture/lifecycle-mechanics-proof.generated.json",
 	"hooks/fixture/lifecycle-mechanics-proof.source.json",
 	"hooks/native-capability-hook",
-]
-const capabilityAssetFiles = ["assets/composer-icon.svg", "assets/logo.svg"]
+].sort(compareCodeUnits)
+const capabilityAssetFiles = ["assets/composer-icon.svg", "assets/logo.svg"].sort(compareCodeUnits)
🤖 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/build.ts` around lines 1628 - 1650, Sort capabilityHookFiles and
capabilityAssetFiles at module scope using the existing compareCodeUnits
ordering, so their literal order matches the sorted inventory used by the
comparisons near the hook and asset validation flow. Keep the existing
skill-list sorting unchanged and ensure both allowlists are normalized once
rather than relying on maintainers to preserve manual order.
scripts/build.test.ts (1)

1576-1586: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Tighten the expected rejection message per path.

The regex /Bun payload closure|unsafe plugin payload entry/ matches any payload-closure failure. A future regression that fails for an unrelated reason still passes this test. Removing skills/capability-tour/references/capability-reviewer.md empties that directory, so pluginPayloadInventory reports an empty directory, while the other two paths report Bun payload closure: missing <path>. Map each path to its exact expected message.

♻️ Proposed refactor
-test.each([
-	"hooks/native-capability-hook",
-	"assets/logo.svg",
-	"skills/capability-tour/references/capability-reviewer.md",
-] as const)("Bun-only release admission rejects missing capability sidecar %s", (path) => {
-	const fixtureRoot = bunPayloadFixture()
-	rmSync(join(fixtureRoot, "plugin", path))
-	expect(() => validateBunOnlyPayload(fixtureRoot)).toThrow(
-		/Bun payload closure|unsafe plugin payload entry/,
-	)
-})
+test.each([
+	["hooks/native-capability-hook", "Bun payload closure: missing hooks/native-capability-hook"],
+	["assets/logo.svg", "Bun payload closure: missing assets/logo.svg"],
+	[
+		"skills/capability-tour/references/capability-reviewer.md",
+		"unsafe plugin payload entry",
+	],
+] as const)("Bun-only release admission rejects missing capability sidecar %s", (path, message) => {
+	const fixtureRoot = bunPayloadFixture()
+	rmSync(join(fixtureRoot, "plugin", path))
+	expect(() => validateBunOnlyPayload(fixtureRoot)).toThrow(message)
+})
🤖 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/build.test.ts` around lines 1576 - 1586, Update the parameterized
test around validateBunOnlyPayload to associate each removed path with its exact
expected rejection message: use the empty-directory message for
skills/capability-tour/references/capability-reviewer.md and the missing-path
Bun payload closure message for the other fixtures. Assert the mapped message
directly instead of using the broad alternation regex.
scripts/release-validate.ts (1)

23-29: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Report every drifted sidecar, not only the first.

Line 28 emits drifted[0]. When several sidecars drift, the maintainer must rerun validation for each one. The static-sidecar loop at Lines 30-34 has the same shape.

♻️ Proposed change
 	if (drifted.length > 0) {
-		throw new Error(`generated capability sidecar differs from its source: ${drifted[0]}`)
+		throw new Error(`generated capability sidecars differ from their source: ${drifted.join(", ")}`)
 	}
🤖 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-validate.ts` around lines 23 - 29, Update the drift error
reporting in the validation flow around checkGeneratedFiles,
checkNativeCapabilityFixture, and the static-sidecar loop to include every
detected drifted sidecar rather than only drifted[0]. Preserve the existing
failure behavior while aggregating all drift paths in the emitted error message.
scripts/prove-harness-install.ts (2)

1434-1471: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Verify the payload hash before you execute the installed hook.

runHandler spawns hooks/native-capability-hook from pluginRoot at Lines 1445 and 1464. The payload-hash comparison runs afterwards at Line 1469. When the candidate payload is a remote checkout (installCandidate in scripts/ship-canary.ts Lines 976-1047), the harness executes candidate-controlled bytes before it confirms those bytes match the expected candidate hash.

Move the runtimeClosureEvidence comparison above the handler execution. The check is cheap and it closes the window.

🛡️ Proposed reordering
 	if (typeof manifest.version !== "string") throw new Error(`${client} installed version is invalid`)
+	const installed = runtimeClosureEvidence(pluginRoot, installedInventory)
+	if (installed.payloadHash !== candidatePayloadHash) {
+		throw new Error(`${client} installed payload hash differs from the candidate payload`)
+	}
 	const handler = join(pluginRoot, "hooks", "native-capability-hook")
@@
 	if (stop.exitCode !== 0 || stop.stdout.toString() !== "" || stop.stderr.toString() !== "") {
 		throw new Error(`${client} installed direct Stop handler check failed`)
 	}
-	const installed = runtimeClosureEvidence(pluginRoot, installedInventory)
-	if (installed.payloadHash !== candidatePayloadHash) {
-		throw new Error(`${client} installed payload hash differs from the candidate payload`)
-	}

Note: the tamper test at scripts/prove-harness-install.test.ts Lines 377-401 still passes after this change.

🤖 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/prove-harness-install.ts` around lines 1434 - 1471, Move the
runtimeClosureEvidence payload-hash validation before the first runHandler
invocation in the installed-hook verification flow. Compute installed evidence
and compare installed.payloadHash with candidatePayloadHash immediately after
defining runHandler, throwing the existing mismatch error before executing
either SessionStart or Stop; remove the later duplicate comparison while
preserving the existing handler checks.

1646-1669: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Add a timeout to the two Git subprocess calls.

command at Line 512 uses timeout: 15_000, and runHandler at Line 1440 now does the same. resolveCleanPathsCommit spawns git status and git rev-parse with no timeout. If either call hangs, the proof blocks with no bound.

♻️ Proposed change
 	const status = Bun.spawnSync({
 		cmd: ["git", "status", "--porcelain=v1", "--untracked-files=all", "--", ...paths],
 		cwd: repositoryRoot,
+		timeout: 15_000,
 		stdout: "pipe",
 		stderr: "pipe",
 	})
@@
 	const head = Bun.spawnSync({
 		cmd: ["git", "rev-parse", "HEAD"],
 		cwd: repositoryRoot,
+		timeout: 15_000,
 		stdout: "pipe",
 		stderr: "pipe",
 	})
🤖 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/prove-harness-install.ts` around lines 1646 - 1669, Update both
Bun.spawnSync calls in resolveCleanPathsCommit, for git status and git
rev-parse, to include the existing 15,000 millisecond timeout used by command
and runHandler. Preserve the current arguments, exit handling, and validation
behavior.
scripts/prove-harness-install.test.ts (1)

377-401: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the specific rejection reason in the tamper test.

Line 390 uses a bare .toThrow(). Any error satisfies it, including an unrelated spawn or parse failure. For assets/logo.svg the only guard is the payload-hash comparison at scripts/prove-harness-install.ts Line 1469. Pin the expected message so the test proves that guard fired.

♻️ Proposed change
-] as const)("installed capability evidence rejects tampered %s bytes", (path) => {
+] as const)("installed capability evidence rejects tampered %s bytes", (path) => {
 	const temporaryRoot = mkdtempSync(join(tmpdir(), "installed-capability-tamper-"))
 	const pluginRoot = join(temporaryRoot, "plugin")
 	try {
 		cpSync(join(root, "plugin"), pluginRoot, { recursive: true })
 		const candidatePayloadHash = runtimeClosureEvidence(pluginRoot).payloadHash
 		const absolutePath = join(pluginRoot, path)
 		writeFileSync(absolutePath, Buffer.concat([readFileSync(absolutePath), Buffer.from("tampered\n")]))
 		expect(() =>
 			proveInstalledCapabilityEvidence(
 				pluginRoot,
 				"claude",
 				"a".repeat(40),
 				candidatePayloadHash,
 			),
-		).toThrow()
+		).toThrow(/installed (payload hash differs|declaration bytes|lifecycle mechanics proof)/)
 	} finally {
🤖 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/prove-harness-install.test.ts` around lines 377 - 401, Update the
tampered-bytes test around proveInstalledCapabilityEvidence to assert the
specific payload-hash mismatch rejection message rather than using a bare
toThrow(). Ensure the assertion verifies the guard in
proveInstalledCapabilityEvidence rejects the modified assets/logo.svg and other
tampered files because their computed payload hash no longer matches the
candidate hash.
🤖 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 `@docs/plans/native-plugin-capability-tour-plan.md`:
- Line 360: Update the acceptance-gate statement in the native plugin capability
tour plan to distinguish the identifiers: require the source commit, archive
checksum, packaged payload hash, and installed payload hash to bind to the same
candidate lineage, while requiring equality only between the packaged and
installed payload hashes.

In `@plugin/skills/capability-tour/SKILL.md`:
- Line 17: Update the direct-handler invocation contract in
plugin/skills/capability-tour/SKILL.md:17-17 and check 4 in
plugin/skills/capability-tour/references/capability-reviewer.md:21-21 to require
the absolute path <installed plugin root>/hooks/native-capability-hook, followed
by exactly the arguments <event> <client>—for this tour, Stop and the active
client. Preserve the existing stdin payload and result-reporting behavior.

In `@scripts/build.ts`:
- Around line 1773-1788: Add "commands" to the forbidden component list used by
the manifest validation loop in the build script, so unsupported top-level
commands declarations trigger the existing Bun payload closure error. Preserve
the current rejection behavior and error format for all other forbidden
components.

In `@scripts/plugin-files.ts`:
- Around line 157-236: Update deterministicPluginArchive to produce identical
USTAR headers on Linux and macOS by using one canonical archive implementation
or explicitly setting matching uname and gname values in both tar argument
paths. Preserve the existing deterministic metadata and gzip flow, and add a
cross-platform digest fixture test before relying on archiveSha256.

In `@scripts/prove-distribution.ts`:
- Around line 165-191: Update the runtime-closure validation around
catalog.skills and bundles.bundles to include the actual sorted surface keys in
the error message when they differ from the expected executable closure.
Preserve the existing comparison and capability-tour context, and report the
computed keys for the failing surface.

In `@scripts/release-validate.test.ts`:
- Around line 219-231: Update the parameterized test “release validation rejects
stale generated capability bytes at %s” to clean up each copyRepository() result
with rmSync in a try/finally block, matching the existing cleanup pattern used
by the nearby test while preserving the validation assertions.

In `@scripts/ship-canary.ts`:
- Around line 1033-1047: Validate manifestVersion as a bounded semantic version
before the deterministicPluginArchive call in the lineage construction,
rejecting traversal or path-like values while preserving Claude/Codex version
agreement checks. Reuse the existing semantic-version validation utility if
available, and ensure invalid versions cannot reach archive staging or payload
copying.

---

Nitpick comments:
In `@plugin/hooks/native-capability-hook`:
- Around line 262-267: Update the +-separator parsing in the identifiers
validation block to avoid relying on split(main, build_parts, "+") treating the
separator literally; use index() with substr() (or an explicitly escaped ERE) to
extract the main version and build metadata, while preserving the existing
identifiers validation and rejection behavior.

In `@scripts/build.test.ts`:
- Around line 1576-1586: Update the parameterized test around
validateBunOnlyPayload to associate each removed path with its exact expected
rejection message: use the empty-directory message for
skills/capability-tour/references/capability-reviewer.md and the missing-path
Bun payload closure message for the other fixtures. Assert the mapped message
directly instead of using the broad alternation regex.

In `@scripts/build.ts`:
- Around line 1628-1650: Sort capabilityHookFiles and capabilityAssetFiles at
module scope using the existing compareCodeUnits ordering, so their literal
order matches the sorted inventory used by the comparisons near the hook and
asset validation flow. Keep the existing skill-list sorting unchanged and ensure
both allowlists are normalized once rather than relying on maintainers to
preserve manual order.

In `@scripts/native-capability-fixture.ts`:
- Around line 71-75: Update scripts/native-capability-fixture.ts:71-75 in
writeNativeCapabilityFixture to create each file’s parent directory recursively
and skip writing when existing contents match. Update scripts/init.ts:194-199 by
removing the duplicated fixture write loop and using the shared writer so both
paths apply the same guards.

In `@scripts/native-capability-hook.test.ts`:
- Around line 533-577: Update the test generation fixture setup around
“generation owns one deterministic LF fixture projection” to create an isolated
temporary root with mkdtempSync, copy plugin/ and plugin.config.json into it,
and point all source/projection paths and runGenerateCheck calls at that root
via cwd. Keep the existing canonical-content and drift assertions, but ensure no
tracked repository files are mutated.

In `@scripts/native-capability-surface.test.ts`:
- Around line 112-117: Remove the single-element outer loop in the test and
reference the "hooks" field directly when validating claudeManifest and
codexManifest. Preserve both existing assertions for the hooks path and file
existence checks.
- Around line 234-264: Update the test named “repository docs separate lifecycle
mechanics from fresh-native qualification” to remove brittle positive assertions
for editorial prose, especially exact wording such as “0700”, “0600”, and “macOS
and Linux POSIX hosts”. Retain the negative overclaim checks and capability
exclusion assertions, and keep only positive claims directly tied to enforced
behavior.

In `@scripts/package.ts`:
- Around line 72-100: Update the archive metadata flow around archiveArtifact
and archiveDigest to derive archiveBytes directly from
archiveArtifact.bytes.length instead of statSync(archive).size, ensuring the
reported size and digest describe the same buffer. Remove statSync from the
node:fs import if it is no longer used.

In `@scripts/plugin-files.ts`:
- Around line 223-234: Update the tar and gzip Bun.spawnSync calls in the
deterministic archive flow to capture child stderr instead of inheriting it,
then include the captured stderr text in each thrown failure Error alongside the
exit code. Preserve stdout behavior and ensure both failure messages identify
the corresponding command.

In `@scripts/prove-harness-install.test.ts`:
- Around line 377-401: Update the tampered-bytes test around
proveInstalledCapabilityEvidence to assert the specific payload-hash mismatch
rejection message rather than using a bare toThrow(). Ensure the assertion
verifies the guard in proveInstalledCapabilityEvidence rejects the modified
assets/logo.svg and other tampered files because their computed payload hash no
longer matches the candidate hash.

In `@scripts/prove-harness-install.ts`:
- Around line 1434-1471: Move the runtimeClosureEvidence payload-hash validation
before the first runHandler invocation in the installed-hook verification flow.
Compute installed evidence and compare installed.payloadHash with
candidatePayloadHash immediately after defining runHandler, throwing the
existing mismatch error before executing either SessionStart or Stop; remove the
later duplicate comparison while preserving the existing handler checks.
- Around line 1646-1669: Update both Bun.spawnSync calls in
resolveCleanPathsCommit, for git status and git rev-parse, to include the
existing 15,000 millisecond timeout used by command and runHandler. Preserve the
current arguments, exit handling, and validation behavior.

In `@scripts/release-projection.test.ts`:
- Around line 36-56: Update the classifyReleaseImpact call in the static
capability sidecars test to include a matching pullRequestIdentity with
expectedAutomationLogin, authorLogin, and a release-please--branches-- head ref.
Keep the existing payloadChanged, isReleasePleaseProjection, and ok expectations
so the assertion exercises the projection path rather than the missing-identity
fallback.

In `@scripts/release-validate.ts`:
- Around line 23-29: Update the drift error reporting in the validation flow
around checkGeneratedFiles, checkNativeCapabilityFixture, and the static-sidecar
loop to include every detected drifted sidecar rather than only drifted[0].
Preserve the existing failure behavior while aggregating all drift paths in the
emitted error message.

In `@scripts/runtime-custody-generation.test.ts`:
- Around line 93-103: Replace the repeated dynamic node:fs imports in the test
with a single top-level static readdirSync import, then update both call sites
in the relevant test flows to call readdirSync directly while preserving their
existing arguments and assertions.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c949b956-8f5b-494a-a778-c8dca4ef343a

📥 Commits

Reviewing files that changed from the base of the PR and between db4d5ff and f360057.

⛔ Files ignored due to path filters (3)
  • plugin/assets/composer-icon.svg is excluded by !**/*.svg
  • plugin/assets/logo.svg is excluded by !**/*.svg
  • plugin/hooks/fixture/lifecycle-mechanics-proof.generated.json is excluded by !**/*.generated.*
📒 Files selected for processing (43)
  • .claude-plugin/marketplace.json
  • CONTEXT.md
  • README.md
  • docs/adr/0005-shared-runtime-custody.md
  • docs/adr/0008-native-plugin-capability-tour.md
  • docs/plans/native-plugin-capability-tour-plan.md
  • plugin.config.json
  • plugin/.claude-plugin/plugin.json
  • plugin/.codex-plugin/plugin.json
  • plugin/hooks/claude/hooks.json
  • plugin/hooks/codex/hooks.json
  • plugin/hooks/fixture/lifecycle-mechanics-proof.source.json
  • plugin/hooks/native-capability-hook
  • plugin/skills/capability-tour/SKILL.md
  • plugin/skills/capability-tour/references/capability-reviewer.md
  • scripts/build.test.ts
  • scripts/build.ts
  • scripts/dev.test.ts
  • scripts/dev.ts
  • scripts/distribution-checksums.test.ts
  • scripts/distribution-checksums.ts
  • scripts/generate.ts
  • scripts/harness-install-codex.test.ts
  • scripts/harness-install-codex.ts
  • scripts/init.test.ts
  • scripts/init.ts
  • scripts/native-capability-fixture.ts
  • scripts/native-capability-hook.test.ts
  • scripts/native-capability-surface.test.ts
  • scripts/package.ts
  • scripts/plugin-config.ts
  • scripts/plugin-files.ts
  • scripts/plugin-manifest-contract.test.ts
  • scripts/prove-distribution.ts
  • scripts/prove-dx.ts
  • scripts/prove-harness-install.test.ts
  • scripts/prove-harness-install.ts
  • scripts/release-projection.test.ts
  • scripts/release-validate.test.ts
  • scripts/release-validate.ts
  • scripts/runtime-custody-generation.test.ts
  • scripts/ship-canary.test.ts
  • scripts/ship-canary.ts

Comment thread docs/plans/native-plugin-capability-tour-plan.md Outdated
Comment thread plugin/skills/capability-tour/SKILL.md Outdated
Comment thread scripts/build.ts
Comment thread scripts/plugin-files.ts
Comment thread scripts/prove-distribution.ts
Comment thread scripts/release-validate.test.ts
Comment thread scripts/ship-canary.ts
@myagentdojo
myagentdojo temporarily deployed to hosted-canary-qualification August 10, 2026 03:28 — with GitHub Actions Inactive
@myagentdojo

myagentdojo commented Aug 10, 2026 •

Copy link
Copy Markdown
Owner Author

Nitpick comments (15)

Addressed across 1328f86 and 185355d as one bounded final batch:

  • shared idempotent fixture writing and isolated generation-drift tests
  • portable build-metadata parsing and direct hook-contract coverage
  • simpler hook assertions with less editorially brittle documentation checks
  • archive size from the digested buffer, one in-process USTAR encoder, and captured gzip diagnostics
  • explicit Release Please identity and normalized allowlists
  • precise sidecar rejection, drift, runtime-closure, and tamper diagnostics
  • installed payload admission before handler execution plus bounded Git subprocesses
  • static filesystem imports and focused temporary-repository cleanup

Full exact-head proof is green: 580 core tests, 61 runtime-custody tests, and native install, distribution, and DX proofs.

@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.

Caution

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

⚠️ Outside diff range comments (2)
scripts/ship-canary.ts (2)

1035-1037: 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

Check the manifest name length before the regex.

manifestName is read from the candidate checkout. Line 1036 evaluates the regex before Line 1037 enforces the 64-character bound. Move the length check before the regex, as validateLineageManifestVersion already does.

Proposed fix
 		if (
 			typeof manifestName !== "string" ||
-			!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(manifestName) ||
-			manifestName.length > 64
+			manifestName.length > 64 ||
+			!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(manifestName)
 		) {
🤖 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/ship-canary.ts` around lines 1035 - 1037, In the manifest-name
validation condition, move the manifestName.length > 64 check before the
regular-expression test, matching the ordering used by
validateLineageManifestVersion. Preserve the existing type check and validation
behavior.

1058-1061: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Bind lineage to the published archive checksum.

deterministicPluginArchive(checkoutRoot, ...) computes a local rebuild digest. It does not prove that the hosted *.checksums.json value matches. Load the sidecar for the exact candidate, compare its archiveSha256 with the rebuilt digest, and reject mismatches before bindCandidateQualificationLineage.

🤖 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/ship-canary.ts` around lines 1058 - 1061, Before
bindCandidateQualificationLineage, load the *.checksums.json sidecar for the
exact candidate and compare its archiveSha256 with the digest returned by
deterministicPluginArchive(checkoutRoot, ...). Reject the candidate on any
mismatch, and only pass the verified hosted checksum into the lineage binding.
🤖 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.

Outside diff comments:
In `@scripts/ship-canary.ts`:
- Around line 1035-1037: In the manifest-name validation condition, move the
manifestName.length > 64 check before the regular-expression test, matching the
ordering used by validateLineageManifestVersion. Preserve the existing type
check and validation behavior.
- Around line 1058-1061: Before bindCandidateQualificationLineage, load the
*.checksums.json sidecar for the exact candidate and compare its archiveSha256
with the digest returned by deterministicPluginArchive(checkoutRoot, ...).
Reject the candidate on any mismatch, and only pass the verified hosted checksum
into the lineage binding.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6d5dc5bf-56b9-416c-8008-7820fe78fbfa

📥 Commits

Reviewing files that changed from the base of the PR and between f360057 and 1328f86.

📒 Files selected for processing (23)
  • docs/plans/native-plugin-capability-tour-plan.md
  • plugin/hooks/native-capability-hook
  • plugin/skills/capability-tour/SKILL.md
  • plugin/skills/capability-tour/references/capability-reviewer.md
  • scripts/build.test.ts
  • scripts/build.ts
  • scripts/init.ts
  • scripts/native-capability-fixture.ts
  • scripts/native-capability-hook.test.ts
  • scripts/native-capability-surface.test.ts
  • scripts/package.ts
  • scripts/plugin-config.ts
  • scripts/plugin-files.test.ts
  • scripts/plugin-files.ts
  • scripts/prove-distribution.ts
  • scripts/prove-harness-install.test.ts
  • scripts/prove-harness-install.ts
  • scripts/release-projection.test.ts
  • scripts/release-validate.test.ts
  • scripts/release-validate.ts
  • scripts/runtime-custody-generation.test.ts
  • scripts/ship-canary.test.ts
  • scripts/ship-canary.ts
🚧 Files skipped from review as they are similar to previous changes (20)
  • plugin/skills/capability-tour/references/capability-reviewer.md
  • scripts/release-projection.test.ts
  • plugin/skills/capability-tour/SKILL.md
  • scripts/init.ts
  • scripts/prove-distribution.ts
  • scripts/release-validate.test.ts
  • scripts/plugin-files.ts
  • scripts/runtime-custody-generation.test.ts
  • scripts/release-validate.ts
  • scripts/package.ts
  • scripts/native-capability-surface.test.ts
  • scripts/build.ts
  • scripts/native-capability-hook.test.ts
  • plugin/hooks/native-capability-hook
  • docs/plans/native-plugin-capability-tour-plan.md
  • scripts/plugin-config.ts
  • scripts/native-capability-fixture.ts
  • scripts/prove-harness-install.ts
  • scripts/build.test.ts
  • scripts/prove-harness-install.test.ts

@myagentdojo
myagentdojo temporarily deployed to hosted-canary-qualification August 10, 2026 03:38 — with GitHub Actions Inactive
@myagentdojo

Copy link
Copy Markdown
Owner Author

@codex review

@myagentdojo

Copy link
Copy Markdown
Owner Author

Resolved the two body-only notes from late CodeRabbit review 4893529244 against current head 185355d:

  • Manifest-name condition: no code change. The existing type guard already short-circuits before both string operations; swapping the regex and length checks changes neither accepted input nor failure behavior.
  • Candidate checksum sidecar: no code change. The protected base driver computes the deterministic archive digest directly over the exact immutable candidate checkout. The sanitized public candidate intentionally has no checksum sidecar, and a candidate-controlled sidecar would not add independent authority. Release and human qualification receipts still bind to the packaged checksum sidecar; hosted canary qualification binds to the trusted driver's measured bytes.

All inline review threads are resolved. Exact-head local proof: 580 pass, 3 skip; runtime custody 61 pass; native install, distribution, and DX green. Exact-head hosted checks are green; Codex rereview is running.

@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: 185355dcd2

ℹ️ 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 plugin/hooks/native-capability-hook
@myagentdojo
myagentdojo temporarily deployed to hosted-canary-qualification August 10, 2026 03:53 — with GitHub Actions Inactive
@myagentdojo

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

Reviewed commit: a40517a84d

ℹ️ 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".

@myagentdojo

Copy link
Copy Markdown
Owner Author

@codex-gate approve a40517a 5235699817

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create an environment for this repo.

@myagentdojo

Copy link
Copy Markdown
Owner Author

@codex-gate approve a40517a 5235699817

@myagentdojo
myagentdojo merged commit bfe8489 into main Aug 10, 2026
18 checks passed
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: a40517a84d

ℹ️ 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".

myagentdojo pushed a commit that referenced this pull request Aug 13, 2026
…rift

The tree enumerates every skill and launcher by hand with nothing generating
those lines, which is how capability-tour stayed missing between #34 and
24102bc. Derives both expected lists from plugin/skills/ and plugin/bin/.

Closes the same defect class the capability-tour inventory guard covers in
e4c6ea4; the README half was left open there.

Sensitivity proven both ways: planting a sixth skill directory turns it RED on
the skills line, planting a fourth launcher turns it RED on the bin line, and
removing each returns GREEN.
myagentdojo added a commit that referenced this pull request Aug 13, 2026
…oc (#42)

* docs: list capability-tour in the architecture tree

The "Architecture at a glance" tree enumerated four skills; plugin/skills/
has five. capability-tour shipped in #34 without updating the diagram.

The line is an exhaustive list, not shorthand: e927c24 (#22) expanded it from
one entry to four when skills were added, and the sibling bin/ line enumerates
all three files in plugin/bin/. The same README already depends on
capability-tour as an existing feature further down.

* docs: list prove:harness-install and guard the skill inventory

Two findings from a full doc-surface audit.

README's proof-command list omitted `prove:harness-install`, the only prove:*
script missing from it. It runs inside `prove:all` and is the named authority
in docs/native-capability-qualification.md for package, declaration, and
installed-byte proof.

capability-tour/SKILL.md enumerates the portable skills in prose with no
generation step behind it, so a new skill silently falsifies the line. That is
the same drift that left capability-tour out of README's architecture tree
until 24102bc. Adds a test deriving the expected inventory from
plugin/skills/ and runtime/skill-catalog.json.

Sensitivity proven: planting a sixth skill directory turns the guard RED on the
missing name; removing it returns GREEN.

* docs: declare what verifies each document

Maps each doc to the artifact that settles its claims, so a docs audit reads
release.yml when checking publishing.md instead of hoping a scanner finds it.
Four docs specify workflow behaviour in roughly 130 assertions between them;
without the map those go unread.

Also declares what this repo cannot check about itself. installing.md is ~90
claims about external Claude and Codex CLI surface, native-capability-
qualification.md rests on human receipts, canary-qualification.md needs a
hosted run. Scanning those returns nothing whether they are accurate or wrong,
so they are marked unverifiable and reported rather than silently passing.

Superseded ADRs 0002 and 0004 and docs/plans/ are frozen - they record history
correctly.

Consumed by the docs-drift skill. Every path verified to resolve.

* test: guard the README architecture tree against skill and launcher drift

The tree enumerates every skill and launcher by hand with nothing generating
those lines, which is how capability-tour stayed missing between #34 and
24102bc. Derives both expected lists from plugin/skills/ and plugin/bin/.

Closes the same defect class the capability-tour inventory guard covers in
e4c6ea4; the README half was left open there.

Sensitivity proven both ways: planting a sixth skill directory turns it RED on
the skills line, planting a fourth launcher turns it RED on the bin line, and
removing each returns GREEN.

* Address PR review feedback (#42)

- Parse the skill inventory instead of probing for each name. The containment
  loop passed while the prose named a skill that does not ship, and passed with
  a model-only skill inside the Bun-backed prefix; both reproduced before the
  change and both now fail. Also asserts every catalog entry ships, which is
  drift in the other direction.
- Add plugin/skills/capability-tour/SKILL.md and scripts/prove-harness-install.ts
  to the README manifest entry. The architecture tree claims capability-tour
  ships and the proof list describes what prove:harness-install checks; neither
  package.json nor runtime.lock.json can settle those.

---------

Co-authored-by: Nathan Vale <hi@nathanvale.com>

This branch was previously deployed

1 inactive deployment
hosted-canary-qualification — a40517a8 Deployed Aug 10, 2026 by myagentdojo via Qualify immutable hosted candidates #118
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.

2 participants