fix(build): make catalogs and packaged skills generated outputs (#1086)
* fix(build): generate catalogs and packaged skills without snapshot commits * fix(build): point skill drift recovery to uncached generation
This commit is contained in:
parent
b49beb42d7
commit
e6134056cf
|
|
@ -28,7 +28,7 @@ intentionally not included. -->
|
|||
- Provider, bridge, target-format, or platform compatibility:
|
||||
- Evidence, artifact, provenance, or reconstruction contract:
|
||||
- Process execution, authorization, cleanup, or containment impact:
|
||||
- Generated metadata (`docs/product-catalog.json`), package, or installation impact (the regen-generated bot pushes tracked generated files; never hand-edit them):
|
||||
- Generated metadata (`docs/public/product-catalog.json`), package, or installation impact (CI generates and validates ignored catalogs and `skills/`; edit authored instructions in `skill-src/` and never commit build outputs):
|
||||
|
||||
## Evidence and regression coverage
|
||||
|
||||
|
|
|
|||
|
|
@ -488,3 +488,13 @@ jobs:
|
|||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run docs:check
|
||||
- name: Generation leaves authored files unchanged
|
||||
run: git diff --exit-code
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: generated-docs
|
||||
path: |
|
||||
docs/public/product-catalog.json
|
||||
docs/verification/managed-conformance-*.json
|
||||
skills/reverse-engineer-anything
|
||||
if-no-files-found: error
|
||||
|
|
|
|||
|
|
@ -1,75 +0,0 @@
|
|||
name: Regenerate generated metadata
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "src/**"
|
||||
- "skills/**/SKILL.md"
|
||||
- "scripts/generate-*.mjs"
|
||||
- "scripts/check-doc-facts.mjs"
|
||||
- "scripts/lib/*.mjs"
|
||||
- "scripts/verify/managed/**"
|
||||
- "scripts/fixtures/managed/**"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
|
||||
concurrency:
|
||||
group: regen-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
regen:
|
||||
name: Regenerate and push tracked generated files
|
||||
# Same-repo PRs only: the default token cannot push to forks.
|
||||
# The bot-actor guard stops looped runs triggered by our own push.
|
||||
if: >-
|
||||
github.actor != 'github-actions[bot]' &&
|
||||
github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run docs:generate
|
||||
- name: Commit regenerated files
|
||||
shell: bash
|
||||
env:
|
||||
HEAD_REF: ${{ github.event.pull_request.head.ref }}
|
||||
run: |
|
||||
generated_paths=(
|
||||
docs/product-catalog.json
|
||||
docs/error-contract.schema.json
|
||||
docs/verification/managed-conformance-manifest.json
|
||||
docs/verification/managed-conformance-ledger.json
|
||||
skills/reverse-engineer-anything/SKILL.md
|
||||
src/generatedPackageMetadata.ts
|
||||
)
|
||||
if git diff --quiet -- "${generated_paths[@]}"; then
|
||||
echo "Generated metadata is already canonical."
|
||||
exit 0
|
||||
fi
|
||||
git diff --check
|
||||
# Refuse to sweep unrelated working-tree changes into the bot commit.
|
||||
unexpected="$(git diff --name-only | grep -vxF -f <(printf '%s\n' "${generated_paths[@]}") || true)"
|
||||
if [[ -n "${unexpected}" ]]; then
|
||||
echo "docs:generate left unexpected tracked changes:" >&2
|
||||
printf '%s\n' "${unexpected}" >&2
|
||||
exit 1
|
||||
fi
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add -- "${generated_paths[@]}"
|
||||
git commit -m "chore: regenerate generated metadata"
|
||||
git push origin "HEAD:${HEAD_REF}"
|
||||
|
|
@ -170,43 +170,9 @@ jobs:
|
|||
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
|
||||
run: npm run docs:generate
|
||||
|
||||
- name: Commit canonical release catalog
|
||||
- name: Validate generated release documentation
|
||||
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
|
||||
shell: bash
|
||||
env:
|
||||
# Use the same identity for the follow-up commit so it also triggers CI.
|
||||
GH_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
RELEASE_PR_JSON: ${{ steps.release.outputs.pr }}
|
||||
run: |
|
||||
release_pr_branch="$(node --input-type=module -e '
|
||||
const value = JSON.parse(process.env.RELEASE_PR_JSON ?? "");
|
||||
if (
|
||||
typeof value !== "object" ||
|
||||
value === null ||
|
||||
typeof value.headBranchName !== "string" ||
|
||||
value.headBranchName.length === 0
|
||||
) {
|
||||
process.exit(1);
|
||||
}
|
||||
process.stdout.write(value.headBranchName);
|
||||
')"
|
||||
changed_paths="$(git diff --name-only)"
|
||||
if [[ -z "${changed_paths}" ]]; then
|
||||
echo "Release documentation is already canonical."
|
||||
exit 0
|
||||
fi
|
||||
if [[ "${changed_paths}" != "docs/product-catalog.json" ]]; then
|
||||
echo "docs:generate changed unexpected tracked paths:" >&2
|
||||
printf '%s\n' "${changed_paths}" >&2
|
||||
exit 1
|
||||
fi
|
||||
git diff --check
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add -- docs/product-catalog.json
|
||||
git commit -m "chore: normalize release product catalog"
|
||||
gh auth setup-git
|
||||
git push origin "HEAD:${release_pr_branch}"
|
||||
run: npm run docs:check && git diff --exit-code
|
||||
|
||||
publish:
|
||||
name: Publish to npm
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ test-results.xml
|
|||
.turbo/
|
||||
.cache/
|
||||
docs/.vitepress/cache/
|
||||
docs/.vitepress/dist/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
|
@ -51,3 +52,10 @@ Thumbs.db
|
|||
|
||||
# Build-generated MCP tool catalog (regenerated by `npm run build`; never commit)
|
||||
src/generatedMcpToolCatalog.ts
|
||||
|
||||
# Derived catalogs, portable conformance projections, and packaged skill bundles
|
||||
# (regenerated from this checkout by `npm run build:cached`; never commit)
|
||||
/skills/
|
||||
/docs/public/product-catalog.json
|
||||
/docs/verification/managed-conformance-manifest.json
|
||||
/docs/verification/managed-conformance-ledger.json
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ REA is a layered ESM TypeScript application. Dependencies flow inward from pure
|
|||
- `src/application/` composes shared CLI/MCP workflows; `src/server/` translates MCP requests; `src/cli.ts` and `src/main.ts` are the CLI and MCP entry points.
|
||||
- `src/process/` owns shared process lifecycle primitives, not provider wire protocols. `bridge/` contains provider-side adapters.
|
||||
- `tests/` contains unit, composition, boundary, acceptance, and conformance tests. `scripts/verify-*` and capability directories under `scripts/verify/` contain real-toolchain checks.
|
||||
- `docs/product-catalog.json` is generated. Update its source contracts and regenerate it; do not edit it directly.
|
||||
- `docs/public/product-catalog.json`, `docs/verification/managed-conformance-*.json`, and `skills/` are ignored build outputs. Update source contracts and authored instructions in `skill-src/`, then run `npm run build:cached`; never commit derived catalog digests or portable conformance projections.
|
||||
- `src/generatedMcpToolCatalog.ts` is build-generated and gitignored. Never commit it; resolve any trace of it in merges by deleting it and running `npm run build:cached`.
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
|
|
@ -36,7 +36,7 @@ REA is a layered ESM TypeScript application. Dependencies flow inward from pure
|
|||
- `npm run check:changed`: run cached static checks and source tests affected since the branch merge base (default `origin/main`).
|
||||
- `npm run check:fast`: run cached typecheck and lint checks.
|
||||
- `npm run check:pr`: opt into the complete local deterministic gate and generated-document checks for broad changes; CI owns full coverage. Routine iterations need focused tests and relevant checks, not the whole gate each time.
|
||||
- `npm run docs:check`: check committed generated documents; `npm run docs:generate` regenerates them.
|
||||
- `npm run docs:check`: build and validate generated documents for the current checkout; `npm run docs:generate` regenerates them. CI retains ignored outputs as artifacts and does not push snapshot commits to feature branches.
|
||||
- For provider-dependent changes, see [docs/testing.md](docs/testing.md) and run the matching real-provider verification.
|
||||
- Keep each verification lane's prerequisites limited to the claim it checks. Use host-native fixtures for host/provider acceptance; put optional cross-target formats and their external toolchains in a separate lane. Preflight required commands and report the missing dependency and lane clearly.
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ links, and `npm run docs:preview` to preview that build at `/rea/`.
|
|||
Site navigation lives in `docs/.vitepress/config.ts`. Keep links to guides
|
||||
relative so they work on GitHub and the website; link to repository files
|
||||
outside `docs/` using their full GitHub URLs. Generated reference documents
|
||||
still use `npm run docs:generate`; `docs:build` only builds the website.
|
||||
use `npm run docs:generate`; `docs:build` generates them before building the website.
|
||||
|
||||
Pull requests run `npm run docs:check`. `.github/workflows/pages.yml` is a
|
||||
manual VitePress build and does not publish. The public site at
|
||||
|
|
@ -74,17 +74,24 @@ require the matching real-provider `verify:*` lane.
|
|||
Formatting uses Oxfmt and the committed `.oxfmtrc.json`; generated sources use
|
||||
the same configuration. Pre-commit formats and lints staged files; pre-push runs
|
||||
`check:fast`.
|
||||
`docs:check` checks committed generated metadata. `docs:generate` regenerates
|
||||
those files, and the docs CI lane checks them.
|
||||
The committed product catalog contains documented facts and their provider
|
||||
`docs:check` builds and validates generated metadata for the current checkout.
|
||||
The product catalog (`docs/public/product-catalog.json`), portable managed
|
||||
conformance projections (`docs/verification/managed-conformance-*.json`), and
|
||||
packaged skill (`skills/`) are ignored build outputs. Edit skill instructions
|
||||
and references in `skill-src/`; the build adds catalog-dependent metadata to
|
||||
the packaged copy without rewriting authored files. The generated manifest
|
||||
commits to that exact packaged skill bundle. It is a portable projection of
|
||||
the deterministic managed verifier, not a record of optional real-provider runs.
|
||||
CI validates these outputs and retains them as artifacts instead of pushing
|
||||
generated-only commits onto feature branches. Reviewed source metadata such as
|
||||
`src/generatedPackageMetadata.ts` and `docs/error-contract.schema.json` remains
|
||||
tracked and checked for freshness. Do not commit ignored generated outputs.
|
||||
The build-generated product catalog contains documented facts and their provider
|
||||
identity, rather than full runtime schema hashes. The managed skill contains
|
||||
instructions and inventory metadata; doctor compares its installed files with
|
||||
the canonical bundle. Schema-only fixes should not change these files or the
|
||||
the canonical bundle. Schema-only fixes should not change these outputs or the
|
||||
skill commitment in the conformance manifest. Runtime schema identity remains
|
||||
available through doctor and `binary_session`. If a branch conflicts only in
|
||||
these generated files, merge the source changes first, then run
|
||||
`npm run docs:generate` to regenerate from the merged sources; keep hand-written
|
||||
skill instructions from both branches.
|
||||
available through doctor and `binary_session`.
|
||||
Real-provider execution remains uncached; deterministic builds use Turbo.
|
||||
|
||||
Local `npm test` runs every deterministic Vitest project without coverage or
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](docs/product-catalog.json)
|
||||
[](docs/mcp-contracts.md#generated-catalog)
|
||||
[](https://nodejs.org/)
|
||||
[](https://skills.sh/morluto/rea/reverse-engineer-anything)
|
||||
[](LICENSE)
|
||||
|
|
@ -296,7 +296,7 @@ For exact options, prerequisites and result contracts:
|
|||
- [Readiness and troubleshooting](docs/installation.md#check-readiness-for-your-task): diagnose one agent or analysis engine.
|
||||
- [CLI and Evidence](docs/cli.md): commands, provider selection, snapshots, import/export and exit statuses.
|
||||
- [MCP contracts](docs/mcp-contracts.md) and [agent prompts](docs/mcp-prompts.md): tool results, sessions and guided investigations.
|
||||
- [Tool catalog](docs/product-catalog.json): generated inventory of tools, providers and CLI commands.
|
||||
- [Tool catalog](docs/mcp-contracts.md#generated-catalog): build-generated inventory of tools, providers and CLI commands.
|
||||
- [Roadmap](docs/roadmap.md): planned work and capability trackers.
|
||||
|
||||
Report vulnerabilities through [SECURITY.md](SECURITY.md).
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ REA_PWNTOOLS_PYTHON=/absolute/isolated-env/bin/python \
|
|||
REA never installs Python, packages or GDB, changes a user init file, launches
|
||||
the selected object as a host process, or requests runtime library resolution. The Python process
|
||||
uses isolated mode and an owned cache. Exact upstream profiles are recorded in
|
||||
[upstream provenance](../third_party/pwntools/README.md).
|
||||
[upstream provenance](https://github.com/morluto/rea/blob/main/third_party/pwntools/README.md).
|
||||
|
||||
## Interpreting results
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ their distinct unavailable-provider or capability classification.
|
|||
The host must already provide util-linux `prlimit`, normally `/usr/bin/prlimit`;
|
||||
`REA_EVM_PRLIMIT_COMMAND` can select another absolute executable. REA installs
|
||||
no system tools and changes no user configuration. [Upstream provenance and
|
||||
license](../third_party/evmole/README.md) are packaged.
|
||||
license](https://github.com/morluto/rea/blob/main/third_party/evmole/README.md) are packaged.
|
||||
|
||||
## Evidence semantics
|
||||
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ npx skills add morluto/rea --skill reverse-engineer-anything
|
|||
|
||||
This installs agent instructions and bundled references, not REA MCP
|
||||
registration or analysis engines. Follow the skill's
|
||||
[conditional connection guide](https://github.com/morluto/rea/blob/main/skills/reverse-engineer-anything/SKILL.md#connect-only-when-needed).
|
||||
[conditional connection guide](https://github.com/morluto/rea/blob/main/skill-src/reverse-engineer-anything/SKILL.md#connect-only-when-needed).
|
||||
Working tools can be used immediately. If tools are missing, inspect the current
|
||||
client's registration with `doctor --client codex --json` (substitute its client
|
||||
ID), then plan repairs with `setup --client codex --dry-run --json`. Show and
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ part of the current tool set.
|
|||
|
||||
This guide describes the implementation and verification of
|
||||
[ADR-0003](adr/0003-managed-code-evidence-and-provider-boundary.md). The canonical
|
||||
tool inventory is [`product-catalog.json`](https://github.com/morluto/rea/blob/main/docs/product-catalog.json).
|
||||
tool inventory is the [build-generated catalog](mcp-contracts.md#generated-catalog).
|
||||
|
||||
## Shipped scope
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,15 @@
|
|||
# MCP runtime contracts
|
||||
|
||||
## Generated catalog
|
||||
|
||||
Run `npm run build:cached` in a source checkout to generate the machine-readable
|
||||
catalog at `docs/public/product-catalog.json`. The [catalog](product-catalog.json)
|
||||
is also included in documentation builds. PR CI retains it with the packaged
|
||||
skill and portable conformance projections in the `generated-docs` artifact.
|
||||
These outputs describe the exact source revision being built; they are not
|
||||
checked-in snapshots. For a running server, `binary_session` remains the
|
||||
authoritative source of catalog identity and tool availability.
|
||||
|
||||
## Identity and discovery
|
||||
|
||||
`binary_session` reports the active package, server, SDK, and negotiated
|
||||
|
|
|
|||
|
|
@ -1,719 +0,0 @@
|
|||
{
|
||||
"package": {
|
||||
"name": "rea-agents",
|
||||
"version": "6.0.0",
|
||||
"sdk": {
|
||||
"server": "2.3.1",
|
||||
"client": "2.3.1",
|
||||
"core": "2.3.1"
|
||||
},
|
||||
"skill_version": "33"
|
||||
},
|
||||
"tools": {
|
||||
"total": 139,
|
||||
"families": [
|
||||
{
|
||||
"id": "direct",
|
||||
"surface": "official-proxy",
|
||||
"count": 41,
|
||||
"tools": [
|
||||
"address_name",
|
||||
"address_to_file_offset",
|
||||
"annotate_native_function",
|
||||
"comment",
|
||||
"current_address",
|
||||
"current_document",
|
||||
"current_procedure",
|
||||
"goto_address",
|
||||
"inline_comment",
|
||||
"inspect_native_data_type",
|
||||
"inspect_native_instruction",
|
||||
"inspect_native_load_image",
|
||||
"list_bookmarks",
|
||||
"list_documents",
|
||||
"list_names",
|
||||
"list_procedures",
|
||||
"list_segments",
|
||||
"list_strings",
|
||||
"next_address",
|
||||
"prev_address",
|
||||
"procedure_address",
|
||||
"procedure_assembly",
|
||||
"procedure_callees",
|
||||
"procedure_callers",
|
||||
"procedure_info",
|
||||
"procedure_pseudo_code",
|
||||
"procedure_references",
|
||||
"read_bytes",
|
||||
"read_function_instructions",
|
||||
"resolve_containing_procedure",
|
||||
"resolve_native_call_targets",
|
||||
"search_procedures",
|
||||
"search_strings",
|
||||
"set_address_name",
|
||||
"set_addresses_names",
|
||||
"set_bookmark",
|
||||
"set_comment",
|
||||
"set_current_document",
|
||||
"set_inline_comment",
|
||||
"unset_bookmark",
|
||||
"xrefs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "enhanced",
|
||||
"surface": "enhanced",
|
||||
"count": 14,
|
||||
"tools": [
|
||||
"analyze_function",
|
||||
"analyze_swift_types",
|
||||
"batch_decompile",
|
||||
"binary_overview",
|
||||
"find_xrefs_to_name",
|
||||
"get_call_graph",
|
||||
"get_objc_classes",
|
||||
"get_objc_protocols",
|
||||
"inspect_native_api",
|
||||
"inspect_native_dispatch_metadata",
|
||||
"trace_call_path",
|
||||
"trace_feature",
|
||||
"trace_native_ui_action",
|
||||
"trace_native_values"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "native",
|
||||
"surface": "native-provider",
|
||||
"count": 10,
|
||||
"tools": [
|
||||
"capture_native_ui_scenario",
|
||||
"demangle_swift",
|
||||
"inspect_binary_layout",
|
||||
"inspect_macho",
|
||||
"inspect_plist",
|
||||
"inspect_recorded_crash",
|
||||
"inspect_signature",
|
||||
"list_architectures",
|
||||
"observe_native_calls",
|
||||
"observe_native_ui"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "artifact",
|
||||
"surface": "artifact-provider",
|
||||
"count": 7,
|
||||
"tools": [
|
||||
"decode_interface_builder",
|
||||
"extract_artifact",
|
||||
"inspect_artifact",
|
||||
"inspect_asset_catalog",
|
||||
"inspect_evm_interface",
|
||||
"inspect_keyed_archive",
|
||||
"trace_dylib_resolution"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "managed",
|
||||
"surface": "managed",
|
||||
"count": 7,
|
||||
"tools": [
|
||||
"compare_managed_members",
|
||||
"import_managed_reconstruction",
|
||||
"inspect_managed_artifact",
|
||||
"inspect_managed_members",
|
||||
"inspect_managed_native_boundaries",
|
||||
"project_managed_application_graph",
|
||||
"verify_managed_native_boundaries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "firmware",
|
||||
"surface": "firmware-provider",
|
||||
"count": 2,
|
||||
"tools": ["extract_firmware", "inspect_firmware_regions"]
|
||||
},
|
||||
{
|
||||
"id": "android",
|
||||
"surface": "android-provider",
|
||||
"count": 5,
|
||||
"tools": [
|
||||
"inspect_android_class",
|
||||
"inspect_android_method",
|
||||
"inspect_android_package",
|
||||
"search_android_classes",
|
||||
"trace_android_references"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "browser",
|
||||
"surface": "browser-provider",
|
||||
"count": 12,
|
||||
"tools": [
|
||||
"analyze_web_bundle",
|
||||
"capture_browser_scenario",
|
||||
"capture_web_screenshot",
|
||||
"compare_web_captures",
|
||||
"compare_web_screenshots",
|
||||
"discover_webmcp_tools",
|
||||
"inspect_web_event_listeners",
|
||||
"inspect_web_network_capture",
|
||||
"inspect_web_page",
|
||||
"list_browser_targets",
|
||||
"observe_web_execution",
|
||||
"observe_web_session"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "electron",
|
||||
"surface": "electron-provider",
|
||||
"count": 5,
|
||||
"tools": [
|
||||
"analyze_javascript_application",
|
||||
"capture_electron_scenario",
|
||||
"inspect_electron_page",
|
||||
"list_electron_targets",
|
||||
"reconcile_javascript_runtime"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "javascript-runtime",
|
||||
"surface": "runtime-provider",
|
||||
"count": 2,
|
||||
"tools": [
|
||||
"list_javascript_runtime_targets",
|
||||
"observe_javascript_runtime"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "application",
|
||||
"surface": "application-workflow",
|
||||
"count": 13,
|
||||
"tools": [
|
||||
"build_reconstruction_obligation_ledger",
|
||||
"compare_application_versions",
|
||||
"compare_javascript_export_shapes",
|
||||
"compare_source_to_bundle",
|
||||
"evaluate_reconstruction_coverage",
|
||||
"export_web_scripts",
|
||||
"project_android_application_graph",
|
||||
"project_apple_application_graph",
|
||||
"recover_javascript_sources",
|
||||
"trace_application_feature",
|
||||
"trace_javascript_semantics",
|
||||
"trace_web_module_imports",
|
||||
"trace_web_source_location"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "session",
|
||||
"surface": "session",
|
||||
"count": 21,
|
||||
"tools": [
|
||||
"binary_session",
|
||||
"build_call_path",
|
||||
"capture_process_scenario",
|
||||
"close_binary",
|
||||
"compare_artifacts",
|
||||
"compare_bundles",
|
||||
"compare_functions",
|
||||
"compare_process_captures",
|
||||
"correlate_static_and_runtime",
|
||||
"export_evidence_bundle",
|
||||
"find_changed_behavior",
|
||||
"get_evidence_bundle",
|
||||
"get_navigation_context",
|
||||
"import_evidence_bundle",
|
||||
"inspect_address_context",
|
||||
"list_unknowns",
|
||||
"open_binary",
|
||||
"record_unknown",
|
||||
"update_unknown",
|
||||
"verify_reconstruction",
|
||||
"verify_unknown_resolution"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"providers": [
|
||||
{
|
||||
"id": "binwalk",
|
||||
"name": "Binwalk",
|
||||
"version": "3.1.0",
|
||||
"capabilities": ["inspect_firmware_regions"]
|
||||
},
|
||||
{
|
||||
"id": "evmole-interface",
|
||||
"name": "REA EVM interface adapter",
|
||||
"version": "evmole@0.9.3",
|
||||
"capabilities": ["inspect_evm_interface"]
|
||||
},
|
||||
{
|
||||
"id": "ghidra",
|
||||
"name": "Ghidra",
|
||||
"version": null,
|
||||
"capabilities": [
|
||||
"address_name",
|
||||
"address_to_file_offset",
|
||||
"analyze_function",
|
||||
"annotate_native_function",
|
||||
"inspect_native_data_type",
|
||||
"inspect_native_instruction",
|
||||
"inspect_native_load_image",
|
||||
"list_documents",
|
||||
"list_names",
|
||||
"list_procedures",
|
||||
"list_segments",
|
||||
"list_strings",
|
||||
"procedure_address",
|
||||
"procedure_assembly",
|
||||
"procedure_callees",
|
||||
"procedure_callers",
|
||||
"procedure_info",
|
||||
"procedure_pseudo_code",
|
||||
"procedure_references",
|
||||
"read_bytes",
|
||||
"read_function_instructions",
|
||||
"resolve_containing_procedure",
|
||||
"resolve_native_call_targets",
|
||||
"search_procedures",
|
||||
"search_strings",
|
||||
"xrefs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "har-schema/lossless-json",
|
||||
"name": "REA HAR capture adapter",
|
||||
"version": "har-schema@2.0.0;jsonc-parser@3.3.1;lossless-json@4.3.1",
|
||||
"capabilities": ["inspect_web_network_capture"]
|
||||
},
|
||||
{
|
||||
"id": "hopper",
|
||||
"name": "Hopper Disassembler",
|
||||
"version": null,
|
||||
"capabilities": [
|
||||
"address_name",
|
||||
"address_to_file_offset",
|
||||
"analyze_function",
|
||||
"comment",
|
||||
"current_address",
|
||||
"current_document",
|
||||
"current_procedure",
|
||||
"goto_address",
|
||||
"inline_comment",
|
||||
"list_bookmarks",
|
||||
"list_documents",
|
||||
"list_names",
|
||||
"list_procedures",
|
||||
"list_segments",
|
||||
"list_strings",
|
||||
"next_address",
|
||||
"prev_address",
|
||||
"procedure_address",
|
||||
"procedure_assembly",
|
||||
"procedure_callees",
|
||||
"procedure_callers",
|
||||
"procedure_info",
|
||||
"procedure_pseudo_code",
|
||||
"procedure_references",
|
||||
"read_bytes",
|
||||
"read_function_instructions",
|
||||
"resolve_containing_procedure",
|
||||
"search_procedures",
|
||||
"search_strings",
|
||||
"set_address_name",
|
||||
"set_addresses_names",
|
||||
"set_bookmark",
|
||||
"set_comment",
|
||||
"set_current_document",
|
||||
"set_inline_comment",
|
||||
"unset_bookmark",
|
||||
"xrefs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ida",
|
||||
"name": "IDA Pro MCP adapter",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"analyze_function",
|
||||
"list_procedures",
|
||||
"list_strings",
|
||||
"procedure_address",
|
||||
"procedure_assembly",
|
||||
"procedure_callees",
|
||||
"procedure_callers",
|
||||
"procedure_pseudo_code",
|
||||
"read_function_instructions",
|
||||
"search_procedures",
|
||||
"search_strings",
|
||||
"xrefs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "jadx",
|
||||
"name": "JADX headless",
|
||||
"version": "0.7.1",
|
||||
"capabilities": [
|
||||
"inspect_android_class",
|
||||
"inspect_android_method",
|
||||
"inspect_android_package",
|
||||
"search_android_classes",
|
||||
"trace_android_references"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "mitmproxy-native-tnetstring",
|
||||
"name": "REA offline native mitmproxy adapter",
|
||||
"version": "12.2.3",
|
||||
"capabilities": ["inspect_web_network_capture"]
|
||||
},
|
||||
{
|
||||
"id": "native-macos",
|
||||
"name": "macOS native inspection utilities",
|
||||
"version": null,
|
||||
"capabilities": [
|
||||
"capture_native_ui_scenario",
|
||||
"demangle_swift",
|
||||
"inspect_macho",
|
||||
"inspect_plist",
|
||||
"inspect_signature",
|
||||
"list_architectures",
|
||||
"observe_native_calls",
|
||||
"observe_native_ui"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "pwntools-elf",
|
||||
"name": "REA pwntools ELF adapter",
|
||||
"version": "pwntools@4.15.0;pyelftools@0.33;unicorn@2.1.2",
|
||||
"capabilities": ["inspect_binary_layout", "inspect_recorded_crash"]
|
||||
},
|
||||
{
|
||||
"id": "rea-android-application",
|
||||
"name": "REA Android application workflows",
|
||||
"version": "1",
|
||||
"capabilities": ["project_android_application_graph"]
|
||||
},
|
||||
{
|
||||
"id": "rea-apple-application",
|
||||
"name": "REA Apple application workflows",
|
||||
"version": "1",
|
||||
"capabilities": ["project_apple_application_graph"]
|
||||
},
|
||||
{
|
||||
"id": "rea-artifact-graph",
|
||||
"name": "REA safe artifact graph provider",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"decode_interface_builder",
|
||||
"extract_artifact",
|
||||
"inspect_artifact",
|
||||
"inspect_asset_catalog",
|
||||
"inspect_keyed_archive",
|
||||
"trace_dylib_resolution"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rea-cdp-browser",
|
||||
"name": "REA Chrome DevTools Protocol observation provider",
|
||||
"version": "2",
|
||||
"capabilities": [
|
||||
"analyze_web_bundle",
|
||||
"capture_web_screenshot",
|
||||
"compare_web_captures",
|
||||
"compare_web_screenshots",
|
||||
"discover_webmcp_tools",
|
||||
"inspect_web_event_listeners",
|
||||
"inspect_web_page",
|
||||
"list_browser_targets",
|
||||
"observe_web_execution",
|
||||
"observe_web_session"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rea-cdp-electron",
|
||||
"name": "REA Electron file-page CDP observation provider",
|
||||
"version": "1",
|
||||
"capabilities": ["inspect_electron_page", "list_electron_targets"]
|
||||
},
|
||||
{
|
||||
"id": "rea-dotnet-static",
|
||||
"name": "REA managed static analysis provider",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"inspect_managed_artifact",
|
||||
"inspect_managed_members",
|
||||
"inspect_managed_native_boundaries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rea-dotnet-workflows",
|
||||
"name": "REA managed-code workflows",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"compare_managed_members",
|
||||
"import_managed_reconstruction",
|
||||
"project_managed_application_graph",
|
||||
"verify_managed_native_boundaries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rea-javascript-application",
|
||||
"name": "REA JavaScript application analyzer",
|
||||
"version": "1",
|
||||
"capabilities": ["analyze_javascript_application"]
|
||||
},
|
||||
{
|
||||
"id": "rea-javascript-application-workflows",
|
||||
"name": "REA JavaScript application workflows",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"build_reconstruction_obligation_ledger",
|
||||
"compare_application_versions",
|
||||
"compare_javascript_export_shapes",
|
||||
"compare_source_to_bundle",
|
||||
"evaluate_reconstruction_coverage",
|
||||
"trace_application_feature",
|
||||
"trace_javascript_semantics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rea-javascript-runtime-reconciliation",
|
||||
"name": "REA JavaScript runtime reconciliation",
|
||||
"version": "1",
|
||||
"capabilities": ["reconcile_javascript_runtime"]
|
||||
},
|
||||
{
|
||||
"id": "rea-playwright-browser-scenario",
|
||||
"name": "REA Playwright browser scenario capture provider",
|
||||
"version": "1",
|
||||
"capabilities": ["capture_browser_scenario"]
|
||||
},
|
||||
{
|
||||
"id": "rea-playwright-electron-active",
|
||||
"name": "REA Playwright active Electron observation provider",
|
||||
"version": "1",
|
||||
"capabilities": ["capture_electron_scenario"]
|
||||
},
|
||||
{
|
||||
"id": "rea-v8-inspector",
|
||||
"name": "REA passive Node/Electron V8 Inspector provider",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"list_javascript_runtime_targets",
|
||||
"observe_javascript_runtime"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rea-web-script-export",
|
||||
"name": "REA captured script export",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"export_web_scripts",
|
||||
"trace_web_module_imports",
|
||||
"trace_web_source_location"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "unblob",
|
||||
"name": "Unblob",
|
||||
"version": "26.6.4",
|
||||
"capabilities": ["extract_firmware"]
|
||||
},
|
||||
{
|
||||
"id": "wakaru",
|
||||
"name": "Wakaru",
|
||||
"version": "1.13.0",
|
||||
"capabilities": ["recover_javascript_sources"]
|
||||
}
|
||||
],
|
||||
"setup_clients": [
|
||||
{
|
||||
"id": "claude_code",
|
||||
"display_name": "Claude Code",
|
||||
"format": "json",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "claude_desktop",
|
||||
"display_name": "Claude Desktop",
|
||||
"format": "json",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "codex",
|
||||
"display_name": "Codex",
|
||||
"format": "toml",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "cursor",
|
||||
"display_name": "Cursor",
|
||||
"format": "json",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "gemini_cli",
|
||||
"display_name": "Gemini CLI",
|
||||
"format": "json",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "windsurf",
|
||||
"display_name": "Windsurf",
|
||||
"format": "json",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "devin",
|
||||
"display_name": "Devin",
|
||||
"format": "json",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "opencode",
|
||||
"display_name": "OpenCode",
|
||||
"format": "opencode",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "antigravity",
|
||||
"display_name": "Antigravity",
|
||||
"format": "json",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "copilot_cli",
|
||||
"display_name": "GitHub Copilot CLI",
|
||||
"format": "copilot_cli",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "commandcode",
|
||||
"display_name": "Command Code",
|
||||
"format": "commandcode",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "vscode",
|
||||
"display_name": "VS Code",
|
||||
"format": "vscode",
|
||||
"configuration": "managed"
|
||||
}
|
||||
],
|
||||
"cli": {
|
||||
"primary_count": 94,
|
||||
"commands": [
|
||||
"address-to-file-offset",
|
||||
"analyze",
|
||||
"analyze-javascript-application",
|
||||
"analyze-web-bundle",
|
||||
"annotate-native-function",
|
||||
"build-reconstruction-obligation-ledger",
|
||||
"capabilities",
|
||||
"capture-browser-scenario",
|
||||
"capture-electron-scenario",
|
||||
"capture-native-ui-scenario",
|
||||
"capture-process",
|
||||
"capture-web-screenshot",
|
||||
"compare",
|
||||
"compare-application-versions",
|
||||
"compare-javascript-export-shapes",
|
||||
"compare-managed-members",
|
||||
"compare-process-captures",
|
||||
"compare-source-to-bundle",
|
||||
"compare-web-captures",
|
||||
"compare-web-screenshots",
|
||||
"decode-interface-builder",
|
||||
"decompile",
|
||||
"demangle-swift",
|
||||
"discover-webmcp-tools",
|
||||
"doctor",
|
||||
"evaluate-reconstruction-coverage",
|
||||
"evidence-export",
|
||||
"evidence-import",
|
||||
"export-web-scripts",
|
||||
"extract-artifact",
|
||||
"extract-firmware",
|
||||
"function",
|
||||
"import-managed-reconstruction",
|
||||
"import-reference-source",
|
||||
"inspect",
|
||||
"inspect-android-class",
|
||||
"inspect-android-method",
|
||||
"inspect-android-package",
|
||||
"inspect-artifact",
|
||||
"inspect-asset-catalog",
|
||||
"inspect-binary-layout",
|
||||
"inspect-electron-page",
|
||||
"inspect-evm-interface",
|
||||
"inspect-firmware-regions",
|
||||
"inspect-keyed-archive",
|
||||
"inspect-macho",
|
||||
"inspect-managed-artifact",
|
||||
"inspect-managed-members",
|
||||
"inspect-managed-native-boundaries",
|
||||
"inspect-native-api",
|
||||
"inspect-native-data-type",
|
||||
"inspect-native-dispatch-metadata",
|
||||
"inspect-native-instruction",
|
||||
"inspect-native-load-image",
|
||||
"inspect-plist",
|
||||
"inspect-recorded-crash",
|
||||
"inspect-signature",
|
||||
"inspect-web-event-listeners",
|
||||
"inspect-web-network-capture",
|
||||
"inspect-web-page",
|
||||
"instructions",
|
||||
"list-architectures",
|
||||
"list-browser-targets",
|
||||
"list-electron-targets",
|
||||
"list-javascript-runtime-targets",
|
||||
"observe-javascript-runtime",
|
||||
"observe-native-calls",
|
||||
"observe-native-ui",
|
||||
"observe-web-execution",
|
||||
"observe-web-session",
|
||||
"project-android-application-graph",
|
||||
"project-apple-application-graph",
|
||||
"project-managed-application-graph",
|
||||
"providers",
|
||||
"read-bytes",
|
||||
"reconcile-javascript-runtime",
|
||||
"recover-javascript-sources",
|
||||
"resolve-native-call-targets",
|
||||
"search",
|
||||
"search-android-classes",
|
||||
"setup",
|
||||
"trace",
|
||||
"trace-android-references",
|
||||
"trace-application-feature",
|
||||
"trace-dylib-resolution",
|
||||
"trace-javascript-semantics",
|
||||
"trace-native-ui-action",
|
||||
"trace-native-values",
|
||||
"trace-web-module-imports",
|
||||
"trace-web-source-location",
|
||||
"uninstall",
|
||||
"update",
|
||||
"verify-managed-native-boundaries",
|
||||
"xrefs"
|
||||
],
|
||||
"aliases": [
|
||||
{
|
||||
"name": "compare-bundles",
|
||||
"target": "compare"
|
||||
}
|
||||
]
|
||||
},
|
||||
"runtime_catalog": {
|
||||
"counts": {
|
||||
"cli_commands": 94,
|
||||
"mcp_tools": 139,
|
||||
"mcp_prompts": 6
|
||||
},
|
||||
"digests": {
|
||||
"providers_sha256": "954b499c76514890062d2b6c936d73cb3a9c45412d522a081125a325e81cdec5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -144,8 +144,8 @@ before cutting the next checkpoint; otherwise main retains the previous
|
|||
release baseline and can propose an already-published version again.
|
||||
|
||||
After publication, open a PR from the release branch back to main. Preserve
|
||||
main's later implementation changes and resolve generated-file conflicts by
|
||||
regenerating from the combined contracts with the released package version.
|
||||
main's later implementation changes and regenerate ignored build outputs from
|
||||
the combined contracts with the released package version.
|
||||
Review and test this synchronization PR, then use a merge commit so the release
|
||||
tag remains in main's ancestry. Keep the released tag unchanged.
|
||||
|
||||
|
|
@ -163,9 +163,12 @@ git merge-base --is-ancestor rea-agents-5.0.0 origin/main
|
|||
|
||||
Close any superseded rolling release PR. Future releases repeat the checkpoint
|
||||
procedure from main; never resume automatic release-PR refreshes on main pushes.
|
||||
The generated-metadata workflow stays limited to pull requests into main so it
|
||||
does not push commits onto a frozen candidate. Preparation normalizes
|
||||
`docs/product-catalog.json` on the bot pull request.
|
||||
Preparation generates and validates the catalog, portable conformance
|
||||
projections, and packaged skill from the candidate checkout without committing
|
||||
them. The checkpoint validator binds tracked version authority to the source
|
||||
SHA; generated-catalog and package checks validate the derived representation.
|
||||
No generated-metadata workflow pushes follow-up commits onto feature or release
|
||||
branches.
|
||||
|
||||
## Partial publication and retries
|
||||
|
||||
|
|
|
|||
|
|
@ -1,105 +0,0 @@
|
|||
{
|
||||
"ledger_id": "ecl_132e857a8a93f0b366cd72f9cfa1c338ffd4100460a7834ef69abff936c84472",
|
||||
"records": [
|
||||
{
|
||||
"claim_id": "managed.application-graph-projection",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_b061a5a559acb3748cb9784c637f6693f74faeb564ba2b11d6fc32619e757459"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.decompiler-reconstruction-import",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_13f3401a3aaddc3f6d087ad160fa928374e142cdb9861254359b46865b8c992e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.dotnet-framework-x86-pinvoke",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_6f026216826ca06e080b77ff608f02634fa945fbd2e151b4cd261c1444f8bbb1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.ilspy-reconstruction-oracle",
|
||||
"status": "unsupported",
|
||||
"evidence_ids": [
|
||||
"ev_65a94c4de03f9c6ebdaff3f381cca377821f349d3ca6dfad47a31cc586fae939"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.malformed-metadata",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_deae4259ea4395623176de40d06c2abcbc2b3a77f0bfb068d76e568d801f45a8"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.managed-native-verification",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_0edcbbcaa1191a4e5324a437f739957b77f9ac45e7897724337f2d6d06b3f9d1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.modern-dotnet-anycpu",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_d1af50e8dc04cd589385769efd8758e82095425f6e9c1cc4bf2f530c6b434c93"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.mvid-and-token-drift",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_5493f40bc875535488d789a490361abcc1f76bfbdf52536d09ff127442a8e854"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.not-managed",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_03ce0cc0183ee0710f8120e837565129559ab3bc41927df326066370c2341699"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.operator-local-manifest",
|
||||
"status": "unsupported",
|
||||
"evidence_ids": [
|
||||
"ev_9d3e50da1d372c55f46649e69eae702cbe8f0ba6a81d1e46afc3e1a284c81757"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.operator-manifest-self-test",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_f974f4b9d039cda841894c6173cae86427ecef48f4e7a4d6c73edf7b23af2b2a"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.unicode-obfuscated-identifiers",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_c4f38ce76d8359d92ee8e9442503e47b7913550442349a0df688bd274a3def67"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.x64-ready-to-run-native-body",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_3e05ef1d6a796d3d4cb3372fe8e549efec759c59bf09d8ff660059a2e448cbc0"
|
||||
]
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"total": 13,
|
||||
"pass": 11,
|
||||
"fail": 0,
|
||||
"unsupported": 2,
|
||||
"truncated": 0,
|
||||
"unknown": 0,
|
||||
"complete": false
|
||||
}
|
||||
}
|
||||
|
|
@ -1,201 +0,0 @@
|
|||
{
|
||||
"manifest_id": "ecm_0abe486e63d1ebdeec6487dfd19a5bd92d47a90d84e60df96926fa66602552ea",
|
||||
"verifier": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
},
|
||||
"environment": {
|
||||
"platform": "portable",
|
||||
"architecture": "portable",
|
||||
"runtime": "node",
|
||||
"runtime_version": "portable"
|
||||
},
|
||||
"skill_digests": [
|
||||
{
|
||||
"skill_id": "reverse-engineer-anything",
|
||||
"sha256": "0e6130b292e3cd3fe1da214b2b3ca5eba8661c81a5bf64679b7ef29828393f29"
|
||||
}
|
||||
],
|
||||
"claims": [
|
||||
{
|
||||
"claim_id": "managed.application-graph-projection",
|
||||
"scenario": {
|
||||
"id": "managed-application-graph-projection",
|
||||
"version": 1
|
||||
},
|
||||
"artifact_sha256s": [
|
||||
"f17fe6286cb07c6385e786e0aa877564d44123197f02617dfec70c537a33e4ea"
|
||||
],
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.decompiler-reconstruction-import",
|
||||
"scenario": {
|
||||
"id": "decompiler-reconstruction-import",
|
||||
"version": 1
|
||||
},
|
||||
"artifact_sha256s": [
|
||||
"cead4cc99733e4e56a5fd0cec8a0813bbc24e249aa72a10eca4c47ee3b0e129d"
|
||||
],
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.dotnet-framework-x86-pinvoke",
|
||||
"scenario": {
|
||||
"id": "dotnet-framework-x86-pinvoke",
|
||||
"version": 1
|
||||
},
|
||||
"artifact_sha256s": [
|
||||
"f17fe6286cb07c6385e786e0aa877564d44123197f02617dfec70c537a33e4ea"
|
||||
],
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.ilspy-reconstruction-oracle",
|
||||
"scenario": {
|
||||
"id": "ilspy-reconstruction-oracle",
|
||||
"version": 1
|
||||
},
|
||||
"artifact_sha256s": [],
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.malformed-metadata",
|
||||
"scenario": {
|
||||
"id": "malformed-metadata",
|
||||
"version": 1
|
||||
},
|
||||
"artifact_sha256s": [
|
||||
"827e10b6391d29edca48c191e2ddd79f3bc1165e46e4ba2f4edf41ab60f45e78"
|
||||
],
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.managed-native-verification",
|
||||
"scenario": {
|
||||
"id": "managed-native-verification",
|
||||
"version": 1
|
||||
},
|
||||
"artifact_sha256s": [
|
||||
"9999999999999999999999999999999999999999999999999999999999999999",
|
||||
"f17fe6286cb07c6385e786e0aa877564d44123197f02617dfec70c537a33e4ea"
|
||||
],
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.modern-dotnet-anycpu",
|
||||
"scenario": {
|
||||
"id": "modern-dotnet-anycpu",
|
||||
"version": 1
|
||||
},
|
||||
"artifact_sha256s": [
|
||||
"92b588ae43d418117fa108c0004b5d0451f6112238d21bad8d75c0eb1da860f0"
|
||||
],
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.mvid-and-token-drift",
|
||||
"scenario": {
|
||||
"id": "mvid-and-token-drift",
|
||||
"version": 1
|
||||
},
|
||||
"artifact_sha256s": [
|
||||
"276395c671a656ab57d583158443c10de552dccfb37dc97cd013f673c0f86b43",
|
||||
"ef009dab7424fdeba996f2524dbe86fd899c96394498cca0463fcfdb228c5572"
|
||||
],
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.not-managed",
|
||||
"scenario": {
|
||||
"id": "not-managed",
|
||||
"version": 1
|
||||
},
|
||||
"artifact_sha256s": [
|
||||
"de74dab8ab034054a811d4248efcda0bed505187a061a806e274ae87bc453754"
|
||||
],
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.operator-local-manifest",
|
||||
"scenario": {
|
||||
"id": "operator-local-manifest",
|
||||
"version": 1
|
||||
},
|
||||
"artifact_sha256s": [],
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.operator-manifest-self-test",
|
||||
"scenario": {
|
||||
"id": "operator-manifest-self-test",
|
||||
"version": 1
|
||||
},
|
||||
"artifact_sha256s": [
|
||||
"cdc11ec7fd0f392e43b9b23971cb41ec49fd0a997fc33c24fcc553cd4386ae37"
|
||||
],
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.unicode-obfuscated-identifiers",
|
||||
"scenario": {
|
||||
"id": "unicode-obfuscated-identifiers",
|
||||
"version": 1
|
||||
},
|
||||
"artifact_sha256s": [
|
||||
"cead4cc99733e4e56a5fd0cec8a0813bbc24e249aa72a10eca4c47ee3b0e129d"
|
||||
],
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.x64-ready-to-run-native-body",
|
||||
"scenario": {
|
||||
"id": "x64-ready-to-run-native-body",
|
||||
"version": 1
|
||||
},
|
||||
"artifact_sha256s": [
|
||||
"e47402428322cdcb06f561036d7d108528cbc6e262d92a21f869218a20c80bfb"
|
||||
],
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -87,7 +87,7 @@
|
|||
"mcp-catalog:check": "node scripts/generate-mcp-tool-catalog.mjs --check",
|
||||
"prebuild": "npm run deps:check && npm run metadata:check",
|
||||
"build": "node scripts/run-exclusive.mjs build npm run build:unlocked",
|
||||
"build:unlocked": "node scripts/clean-build-output.mjs && tsc -p tsconfig.build.json && node scripts/generate-mcp-tool-catalog.mjs",
|
||||
"build:unlocked": "node scripts/clean-build-output.mjs && tsc -p tsconfig.build.json && node scripts/generate-mcp-tool-catalog.mjs && node scripts/generate-skill-metadata.mjs && node scripts/generate-product-catalog.mjs && node scripts/generate-completion-ledger.mjs",
|
||||
"precheck": "npm run deps:check",
|
||||
"check": "turbo run typecheck lint format:check knip metadata:check",
|
||||
"precheck:fast": "npm run deps:check",
|
||||
|
|
@ -179,7 +179,9 @@
|
|||
"config:print": "node scripts/print-mcp-config.mjs",
|
||||
"evidence:generate": "npm run build:cached && node scripts/generate-completion-ledger.mjs",
|
||||
"evidence:check": "npm run build:cached && node scripts/generate-completion-ledger.mjs --check",
|
||||
"predocs:dev": "npm run docs:generate",
|
||||
"docs:dev": "vitepress dev docs",
|
||||
"predocs:build": "npm run docs:generate",
|
||||
"docs:build": "vitepress build docs",
|
||||
"docs:preview": "vitepress preview docs",
|
||||
"docs:generate": "node scripts/run-exclusive.mjs docs npm run docs:generate:unlocked",
|
||||
|
|
|
|||
|
|
@ -34,11 +34,6 @@
|
|||
"type": "json",
|
||||
"path": "server.json",
|
||||
"jsonpath": "$.packages[0].version"
|
||||
},
|
||||
{
|
||||
"type": "json",
|
||||
"path": "docs/product-catalog.json",
|
||||
"jsonpath": "$.package.version"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ const { stdout } = await exec(
|
|||
},
|
||||
);
|
||||
const verifierOutput = JSON.parse(stdout);
|
||||
// Managed conformance claims are host-independent. Keep the checked-in
|
||||
// Managed conformance claims are host-independent. Keep the generated
|
||||
// commitment reproducible across macOS, Linux, and Windows rather than
|
||||
// embedding the machine that happened to generate it.
|
||||
const portableCompletionReport = {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const lock = JSON.parse(
|
|||
await readFile(join(root, "package-lock.json"), "utf8"),
|
||||
);
|
||||
const skill = await readFile(
|
||||
join(root, "skills/reverse-engineer-anything/SKILL.md"),
|
||||
join(root, "skill-src/reverse-engineer-anything/SKILL.md"),
|
||||
"utf8",
|
||||
);
|
||||
const skillVersion = /^\s{2}version:\s*"([^"]+)"\s*$/mu.exec(skill)?.[1];
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { mkdir } from "node:fs/promises";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
|
|
@ -14,8 +15,10 @@ for (const argument of arguments_)
|
|||
|
||||
const root = join(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const catalog = await createProductCatalog(root);
|
||||
const path = join(root, "docs/public/product-catalog.json");
|
||||
if (!arguments_.has("--check")) await mkdir(dirname(path), { recursive: true });
|
||||
await ensureGeneratedFile({
|
||||
path: join(root, "docs/product-catalog.json"),
|
||||
path,
|
||||
source: await serializeProductCatalog(catalog),
|
||||
check: arguments_.has("--check"),
|
||||
generateCommand: "npm run docs:generate",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { readFile } from "node:fs/promises";
|
||||
import { mkdir, readFile, readdir, rm } from "node:fs/promises";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
|
||||
|
|
@ -10,25 +10,69 @@ for (const argument of arguments_)
|
|||
throw new Error(`Unknown skill metadata option: ${argument}`);
|
||||
|
||||
const root = join(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const path = join(root, "skills/reverse-engineer-anything/SKILL.md");
|
||||
const current = await readFile(path, "utf8");
|
||||
const sourceRoot = join(root, "skill-src/reverse-engineer-anything");
|
||||
const outputRoot = join(root, "skills/reverse-engineer-anything");
|
||||
const check = arguments_.has("--check");
|
||||
const current = (
|
||||
await readFile(join(sourceRoot, "SKILL.md"), "utf8")
|
||||
).replaceAll("\r\n", "\n");
|
||||
const { CATALOG_IDENTITY } = await import(
|
||||
`${pathToFileURL(join(root, "dist/catalogIdentity.js")).href}?${String(Date.now())}`
|
||||
);
|
||||
const withCount = current.replace(
|
||||
/^\s{2}tool_count:\s*\d+\s*$/mu,
|
||||
` tool_count: ${String(CATALOG_IDENTITY.counts.mcp_tools)}`,
|
||||
);
|
||||
// Skill identity follows the installed instruction bundle. Runtime schema identity
|
||||
// is still reported independently by doctor and binary_session.
|
||||
const source = withCount.replace(
|
||||
/^ {2}catalog_digest:[^\r\n]*(?:\r?\n|$)/mu,
|
||||
"",
|
||||
if (/^\s{2}(?:tool_count|catalog_digest):/mu.test(current))
|
||||
throw new Error(
|
||||
"Catalog metadata belongs in the generated skill, not skill-src",
|
||||
);
|
||||
const versionLine = /^ {2}version: "[^"\r\n]+"$/mu;
|
||||
if (!versionLine.test(current))
|
||||
throw new Error("Missing authored skill version");
|
||||
const source = current.replace(
|
||||
versionLine,
|
||||
`$&\n tool_count: ${String(CATALOG_IDENTITY.counts.mcp_tools)}`,
|
||||
);
|
||||
const paths = await filePaths(sourceRoot);
|
||||
// Rebuild this owned output directory so removed references cannot survive a build.
|
||||
if (!check) await rm(outputRoot, { recursive: true, force: true });
|
||||
for (const relativePath of paths) {
|
||||
const path = join(outputRoot, relativePath);
|
||||
if (!check) await mkdir(dirname(path), { recursive: true });
|
||||
await ensureGeneratedFile({
|
||||
path,
|
||||
source:
|
||||
relativePath === "SKILL.md"
|
||||
? source
|
||||
: (await readFile(join(sourceRoot, relativePath), "utf8")).replaceAll(
|
||||
"\r\n",
|
||||
"\n",
|
||||
),
|
||||
check,
|
||||
generateCommand: "npm run docs:generate",
|
||||
});
|
||||
}
|
||||
if (
|
||||
check &&
|
||||
JSON.stringify(await filePaths(outputRoot)) !== JSON.stringify(paths)
|
||||
)
|
||||
throw new Error(
|
||||
"Generated skill file inventory drifted; run npm run docs:generate",
|
||||
);
|
||||
|
||||
await ensureGeneratedFile({
|
||||
path,
|
||||
source,
|
||||
check: arguments_.has("--check"),
|
||||
generateCommand: "npm run docs:generate",
|
||||
});
|
||||
async function filePaths(directory, prefix = "") {
|
||||
const paths = [];
|
||||
for (const entry of (await readdir(directory, { withFileTypes: true })).sort(
|
||||
(left, right) =>
|
||||
left.name < right.name ? -1 : left.name > right.name ? 1 : 0,
|
||||
)) {
|
||||
const relativePath = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
||||
if (entry.isDirectory())
|
||||
paths.push(
|
||||
...(await filePaths(join(directory, entry.name), relativePath)),
|
||||
);
|
||||
else if (entry.isFile()) paths.push(relativePath);
|
||||
else
|
||||
throw new Error(
|
||||
`Skill bundle requires regular files or directories: ${relativePath}`,
|
||||
);
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,12 @@ export const documentationFactIssues = async (root, catalog) => {
|
|||
requireText(issues, path, content, "MCP-tool_catalog");
|
||||
if (path === "README.md") {
|
||||
requireText(issues, path, content, "(docs/installation.md");
|
||||
requireText(issues, path, content, "(docs/product-catalog.json)");
|
||||
requireText(
|
||||
issues,
|
||||
path,
|
||||
content,
|
||||
"(docs/mcp-contracts.md#generated-catalog)",
|
||||
);
|
||||
continue;
|
||||
}
|
||||
for (const client of catalog.setup_clients)
|
||||
|
|
@ -99,11 +104,16 @@ export const documentationFactIssues = async (root, catalog) => {
|
|||
requireText(issues, installationPath, installation, client.display_name);
|
||||
|
||||
const agents = await readFile(join(root, "AGENTS.md"), "utf8");
|
||||
requireText(issues, "AGENTS.md", agents, "docs/product-catalog.json");
|
||||
requireText(issues, "AGENTS.md", agents, "docs/public/product-catalog.json");
|
||||
|
||||
const templatePath = ".github/pull_request_template.md";
|
||||
const template = await readFile(join(root, templatePath), "utf8");
|
||||
requireText(issues, templatePath, template, "docs/product-catalog.json");
|
||||
requireText(
|
||||
issues,
|
||||
templatePath,
|
||||
template,
|
||||
"docs/public/product-catalog.json",
|
||||
);
|
||||
|
||||
return issues;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -65,9 +65,9 @@ export const createProductCatalog = async (root) => {
|
|||
};
|
||||
};
|
||||
|
||||
/** Stable checked-in representation of the product catalog. */
|
||||
/** Stable build-generated representation of the product catalog. */
|
||||
export const serializeProductCatalog = (catalog) =>
|
||||
formatGeneratedFile(
|
||||
"docs/product-catalog.json",
|
||||
"docs/public/product-catalog.json",
|
||||
JSON.stringify(catalog, null, 2),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -180,13 +180,8 @@ async function validateCandidate(git, sha, version, commits) {
|
|||
["server.json", server.version],
|
||||
["server.json rea-agents package", npmPackage.version],
|
||||
);
|
||||
const catalog = await readJson(
|
||||
git,
|
||||
sha,
|
||||
"docs/product-catalog.json",
|
||||
z.object({ package: versionSchema }),
|
||||
);
|
||||
versions.push(["docs/product-catalog.json", catalog.package.version]);
|
||||
// Catalogs are generated and checked from this candidate by the release job;
|
||||
// they are not version authority stored in the immutable source tree.
|
||||
const generated = await git([
|
||||
"show",
|
||||
`${sha}:src/generatedPackageMetadata.ts`,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { join } from "node:path";
|
||||
import { CATALOG_IDENTITY } from "../dist/catalogIdentity.js";
|
||||
|
||||
import { exec } from "./lib/verify-package-core.mjs";
|
||||
import { verifyPackedBridge } from "./verify-packed-bridge.mjs";
|
||||
|
|
@ -28,6 +29,26 @@ export async function verifyPackagePack({ root, workspace }) {
|
|||
throw new Error("package did not expose both rea command entry points");
|
||||
if (packedManifest.mcpName !== "io.github.morluto/rea")
|
||||
throw new Error("package did not retain its MCP Registry ownership marker");
|
||||
if (packedFiles.some((path) => path.startsWith("package/skill-src/")))
|
||||
throw new Error(
|
||||
"package included authored skill sources instead of only the generated bundle",
|
||||
);
|
||||
const skill = (
|
||||
await exec("tar", [
|
||||
"-xOf",
|
||||
join(root, tarball),
|
||||
"package/skills/reverse-engineer-anything/SKILL.md",
|
||||
])
|
||||
).stdout;
|
||||
if (
|
||||
!skill.includes(
|
||||
` tool_count: ${String(CATALOG_IDENTITY.counts.mcp_tools)}\n`,
|
||||
) ||
|
||||
skill.includes(" catalog_digest:")
|
||||
)
|
||||
throw new Error(
|
||||
"packaged skill metadata did not match the catalog shipped in the package",
|
||||
);
|
||||
if (
|
||||
packedFiles.some(
|
||||
(path) => path.includes("__pycache__") || path.endsWith(".pyc"),
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ name: reverse-engineer-anything
|
|||
description: Reverse engineer native, managed, Electron/JavaScript, packaged, firmware, and browser targets with REA. Use shipped-artifact or requested runtime evidence to explain features, compare versions, decompile code, or guide a reconstruction. Skip REA for ordinary source-repository architecture analysis.
|
||||
metadata:
|
||||
version: "33"
|
||||
tool_count: 139
|
||||
---
|
||||
|
||||
# REA
|
||||
|
|
@ -95,7 +95,7 @@ describe("canonical product catalog", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("matches every source-derived checked-in product fact", async () => {
|
||||
it("matches every source-derived build-generated product fact", async () => {
|
||||
const catalog = await createProductCatalog(root);
|
||||
expect(catalog.tools.total).toBe(TOOL_CONTRACTS.length);
|
||||
expect(
|
||||
|
|
@ -204,10 +204,10 @@ describe("canonical product catalog", () => {
|
|||
providerCatalogDigest(catalog.providers),
|
||||
);
|
||||
expect(
|
||||
JSON.parse(await readFile("docs/product-catalog.json", "utf8")),
|
||||
JSON.parse(await readFile("docs/public/product-catalog.json", "utf8")),
|
||||
).toEqual(catalog);
|
||||
expect(await serializeProductCatalog(catalog)).toBe(
|
||||
await readFile("docs/product-catalog.json", "utf8"),
|
||||
await readFile("docs/public/product-catalog.json", "utf8"),
|
||||
);
|
||||
await expect(
|
||||
assertDocumentationFacts(root, catalog),
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ const createBuildCacheFixture = async (): Promise<string> => {
|
|||
"scripts/clean-build-output.mjs",
|
||||
"scripts/generate-package-metadata.mjs",
|
||||
"scripts/lib/generated-file.mjs",
|
||||
"skills/reverse-engineer-anything/SKILL.md",
|
||||
"skill-src/reverse-engineer-anything/SKILL.md",
|
||||
"src/generatedPackageMetadata.ts",
|
||||
];
|
||||
await Promise.all(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,125 @@
|
|||
import { execFile } from "node:child_process";
|
||||
import { copyFile, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { promisify } from "node:util";
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
|
||||
const exec = promisify(execFile);
|
||||
const repositoryRoot = fileURLToPath(new URL("../../..", import.meta.url));
|
||||
const authored =
|
||||
'---\nname: reverse-engineer-anything\nmetadata:\n version: "33"\n---\n\n# Instructions\n\nInspect the caller-selected artifact.\n';
|
||||
|
||||
async function fixture(eol = "\n") {
|
||||
const root = await createTestTempDirectory("rea-generated-skill-");
|
||||
for (const path of [
|
||||
"scripts/generate-skill-metadata.mjs",
|
||||
"scripts/lib/generated-file.mjs",
|
||||
]) {
|
||||
await mkdir(dirname(join(root, path)), { recursive: true });
|
||||
await copyFile(join(repositoryRoot, path), join(root, path));
|
||||
}
|
||||
const sourceRoot = join(root, "skill-src/reverse-engineer-anything");
|
||||
const outputRoot = join(root, "skills/reverse-engineer-anything");
|
||||
await mkdir(join(sourceRoot, "references"), { recursive: true });
|
||||
await mkdir(join(root, "dist"));
|
||||
await writeFile(join(root, "package.json"), '{"type":"module"}\n');
|
||||
await writeFile(join(sourceRoot, "SKILL.md"), authored.replaceAll("\n", eol));
|
||||
await writeFile(
|
||||
join(sourceRoot, "references/guide.md"),
|
||||
`# Guide${eol}Preserve evidence.${eol}`,
|
||||
);
|
||||
const identity = async (count: number, digest: string) =>
|
||||
writeFile(
|
||||
join(root, "dist/catalogIdentity.js"),
|
||||
`export const CATALOG_IDENTITY = ${JSON.stringify({ counts: { mcp_tools: count }, digests: { combined_sha256: digest } })};\n`,
|
||||
);
|
||||
await identity(139, "a".repeat(64));
|
||||
const generate = (...args: string[]) =>
|
||||
exec(
|
||||
process.execPath,
|
||||
[join(root, "scripts/generate-skill-metadata.mjs"), ...args],
|
||||
{ cwd: root },
|
||||
);
|
||||
return { sourceRoot, outputRoot, identity, generate };
|
||||
}
|
||||
|
||||
it.each(["\n", "\r\n"])(
|
||||
"generates revision-specific metadata without rewriting authored instructions (%j)",
|
||||
async (eol) => {
|
||||
const f = await fixture(eol);
|
||||
await f.generate();
|
||||
const first = await readFile(join(f.outputRoot, "SKILL.md"), "utf8");
|
||||
expect(first).not.toContain("catalog_digest:");
|
||||
expect(first).toContain("tool_count: 139");
|
||||
expect(
|
||||
first.replace(/^ {2}(?:tool_count|catalog_digest):[^\n]*\n/gmu, ""),
|
||||
).toBe(authored);
|
||||
expect(
|
||||
await readFile(join(f.outputRoot, "references/guide.md"), "utf8"),
|
||||
).toBe("# Guide\nPreserve evidence.\n");
|
||||
await f.identity(139, "b".repeat(64));
|
||||
await f.generate();
|
||||
expect(await readFile(join(f.outputRoot, "SKILL.md"), "utf8")).toBe(first);
|
||||
await f.identity(140, "b".repeat(64));
|
||||
await expect(f.generate("--check")).rejects.toMatchObject({
|
||||
stderr: expect.stringContaining("missing or stale"),
|
||||
});
|
||||
await f.generate();
|
||||
expect(await readFile(join(f.outputRoot, "SKILL.md"), "utf8")).toContain(
|
||||
"tool_count: 140",
|
||||
);
|
||||
expect(await readFile(join(f.sourceRoot, "SKILL.md"), "utf8")).toBe(
|
||||
authored.replaceAll("\n", eol),
|
||||
);
|
||||
expect(
|
||||
await readFile(join(f.sourceRoot, "references/guide.md"), "utf8"),
|
||||
).toBe(`# Guide${eol}Preserve evidence.${eol}`);
|
||||
await expect(f.generate("--check")).resolves.toMatchObject({ stderr: "" });
|
||||
},
|
||||
);
|
||||
|
||||
it("rejects missing, stale, and extra packaged references and removes deleted source files on rebuild", async () => {
|
||||
const f = await fixture();
|
||||
await f.generate();
|
||||
const guide = join(f.outputRoot, "references/guide.md");
|
||||
await rm(guide);
|
||||
await expect(f.generate("--check")).rejects.toMatchObject({
|
||||
stderr: expect.stringContaining("guide.md is missing or stale"),
|
||||
});
|
||||
await f.generate();
|
||||
await writeFile(guide, "modified reference\n");
|
||||
await expect(f.generate("--check")).rejects.toMatchObject({
|
||||
stderr: expect.stringContaining("guide.md is missing or stale"),
|
||||
});
|
||||
await f.generate();
|
||||
await writeFile(
|
||||
join(f.outputRoot, "references/removed.md"),
|
||||
"obsolete instructions\n",
|
||||
);
|
||||
await expect(f.generate("--check")).rejects.toMatchObject({
|
||||
stderr: expect.stringContaining("file inventory drifted"),
|
||||
});
|
||||
await rm(join(f.sourceRoot, "references/guide.md"));
|
||||
await f.generate();
|
||||
await expect(readFile(guide)).rejects.toMatchObject({ code: "ENOENT" });
|
||||
await expect(
|
||||
readFile(join(f.outputRoot, "references/removed.md")),
|
||||
).rejects.toMatchObject({ code: "ENOENT" });
|
||||
await f.generate("--check");
|
||||
});
|
||||
|
||||
it("rejects catalog-dependent metadata in the authored source", async () => {
|
||||
const f = await fixture();
|
||||
await writeFile(
|
||||
join(f.sourceRoot, "SKILL.md"),
|
||||
authored.replace(' version: "33"', ' version: "33"\n tool_count: 139'),
|
||||
);
|
||||
await expect(f.generate()).rejects.toMatchObject({
|
||||
stderr: expect.stringContaining(
|
||||
"Catalog metadata belongs in the generated skill",
|
||||
),
|
||||
});
|
||||
});
|
||||
|
|
@ -50,7 +50,6 @@ async function metadata(directory: string, version: string, notes: string) {
|
|||
version,
|
||||
packages: [{ identifier: "rea-agents", version }],
|
||||
},
|
||||
"docs/product-catalog.json": { package: { version } },
|
||||
"release-please-config.json": {
|
||||
"changelog-sections": [
|
||||
{ type: "feat", hidden: false },
|
||||
|
|
@ -371,7 +370,6 @@ it("validates candidate options before inspecting the working directory", async
|
|||
it.each([
|
||||
"package-lock.json",
|
||||
"server.json",
|
||||
"docs/product-catalog.json",
|
||||
"src/generatedPackageMetadata.ts",
|
||||
])("rejects inconsistent version metadata in %s", async (path) => {
|
||||
const f = await fixture();
|
||||
|
|
|
|||
|
|
@ -103,11 +103,11 @@ it("requires explicit release preparation or publication instead of main pushes"
|
|||
"skip-github-release": "${{ inputs.phase == 'prepare' }}",
|
||||
"skip-github-pull-request": "${{ inputs.phase == 'publish' }}",
|
||||
});
|
||||
const catalogCommit = workflow.jobs["release-please"].steps.find(
|
||||
(step) => step.name === "Commit canonical release catalog",
|
||||
const catalogValidation = workflow.jobs["release-please"].steps.find(
|
||||
(step) => step.name === "Validate generated release documentation",
|
||||
);
|
||||
expect(catalogCommit?.env?.GH_TOKEN).toBe(
|
||||
"${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}",
|
||||
expect(catalogValidation?.run).toBe(
|
||||
"npm run docs:check && git diff --exit-code",
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -203,7 +203,7 @@ it("binds npm and MCP publication to the same immutable release SHA", async () =
|
|||
"Set up Node.js for generated documentation",
|
||||
"Install dependencies",
|
||||
"Regenerate release documentation",
|
||||
"Commit canonical release catalog",
|
||||
"Validate generated release documentation",
|
||||
];
|
||||
for (const name of preparation) {
|
||||
expect(
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { createHash } from "node:crypto";
|
||||
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { dirname, join } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
|
@ -14,6 +15,7 @@ import {
|
|||
import { createDoctorHostFixture } from "../../../src/application/Doctor.fixture.js";
|
||||
import { TOOL_CONTRACTS } from "../../../src/contracts/toolContracts.js";
|
||||
import { PRODUCT_IDENTITY } from "../../../src/identity.js";
|
||||
import { z } from "zod";
|
||||
import { skillReferenceIssues } from "../../../scripts/lib/docs-facts.mjs";
|
||||
|
||||
describe("canonical skill transaction", () => {
|
||||
|
|
@ -88,6 +90,7 @@ describe("canonical skill transaction", () => {
|
|||
expect(installedSkill).toContain(
|
||||
`tool_count: ${String(TOOL_CONTRACTS.length)}`,
|
||||
);
|
||||
expect(installedSkill).not.toContain("catalog_digest:");
|
||||
expect(await readFile(sibling, "utf8")).toBe("unrelated skill\n");
|
||||
expect(
|
||||
await readFile(
|
||||
|
|
@ -102,6 +105,45 @@ describe("canonical skill transaction", () => {
|
|||
expect(await installCanonicalSkill(home)).toBe("unchanged");
|
||||
expect(await skillReferenceIssues(dirname(destination))).toEqual([]);
|
||||
});
|
||||
|
||||
it("binds portable conformance to the generated skill bytes that setup installs", async () => {
|
||||
const home = await createTestTempDirectory("rea-skill-commitment-");
|
||||
expect(await installCanonicalSkill(home)).toBe("installed");
|
||||
const paths = [
|
||||
"SKILL.md",
|
||||
"references/android-applications.md",
|
||||
"references/evidence-workflows.md",
|
||||
"references/javascript-applications.md",
|
||||
"references/native-and-artifacts.md",
|
||||
"references/runtime-observation.md",
|
||||
];
|
||||
const records = await Promise.all(
|
||||
paths.map(async (path) => {
|
||||
const bytes = await readFile(
|
||||
join(home, ".agents/skills/reverse-engineer-anything", path),
|
||||
);
|
||||
return `${path}\0${createHash("sha256").update(bytes).digest("hex")}\n`;
|
||||
}),
|
||||
);
|
||||
const manifest = z
|
||||
.object({
|
||||
skill_digests: z.array(
|
||||
z.object({ skill_id: z.string(), sha256: z.string() }),
|
||||
),
|
||||
})
|
||||
.parse(
|
||||
JSON.parse(
|
||||
await readFile(
|
||||
"docs/verification/managed-conformance-manifest.json",
|
||||
"utf8",
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(manifest.skill_digests).toContainEqual({
|
||||
skill_id: "reverse-engineer-anything",
|
||||
sha256: createHash("sha256").update(records.join("")).digest("hex"),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("doctor verifies installed instructions and references even when metadata is current", async () => {
|
||||
|
|
|
|||
|
|
@ -47,11 +47,6 @@ describe("release configuration", () => {
|
|||
path: "server.json",
|
||||
jsonpath: "$.packages[0].version",
|
||||
},
|
||||
{
|
||||
type: "json",
|
||||
path: "docs/product-catalog.json",
|
||||
jsonpath: "$.package.version",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
|
|||
19
turbo.json
19
turbo.json
|
|
@ -10,8 +10,14 @@
|
|||
"scripts/clean-build-output.mjs",
|
||||
"scripts/generate-mcp-tool-catalog.mjs",
|
||||
"scripts/generate-package-metadata.mjs",
|
||||
"scripts/lib/generated-file.mjs",
|
||||
"skills/reverse-engineer-anything/SKILL.md",
|
||||
"scripts/generate-skill-metadata.mjs",
|
||||
"scripts/generate-product-catalog.mjs",
|
||||
"scripts/generate-completion-ledger.mjs",
|
||||
"scripts/lib/**/*.mjs",
|
||||
"scripts/verify/managed/**",
|
||||
"scripts/fixtures/managed/**",
|
||||
".oxfmtrc.json",
|
||||
"skill-src/**",
|
||||
"src/**/*.ts",
|
||||
"!src/**/*.test.ts",
|
||||
"!src/**/*.fixture.ts",
|
||||
|
|
@ -22,7 +28,11 @@
|
|||
"outputs": [
|
||||
"dist/**",
|
||||
".cache/tsconfig.build.tsbuildinfo",
|
||||
"src/generatedMcpToolCatalog.ts"
|
||||
"src/generatedMcpToolCatalog.ts",
|
||||
"skills/**",
|
||||
"docs/public/product-catalog.json",
|
||||
"docs/verification/managed-conformance-manifest.json",
|
||||
"docs/verification/managed-conformance-ledger.json"
|
||||
]
|
||||
},
|
||||
"typecheck": {
|
||||
|
|
@ -46,7 +56,7 @@
|
|||
"package-lock.json",
|
||||
"scripts/generate-package-metadata.mjs",
|
||||
"scripts/lib/generated-file.mjs",
|
||||
"skills/reverse-engineer-anything/SKILL.md",
|
||||
"skill-src/reverse-engineer-anything/SKILL.md",
|
||||
"src/generatedPackageMetadata.ts"
|
||||
]
|
||||
},
|
||||
|
|
@ -124,6 +134,7 @@
|
|||
"src/generated*.ts",
|
||||
"src/contracts/**/*.ts",
|
||||
"docs/**",
|
||||
"skill-src/**",
|
||||
"skills/**/SKILL.md",
|
||||
"README.md"
|
||||
]
|
||||
|
|
|
|||
Loading…
Reference in New Issue