Files
rea/src/contracts/analysisViewEnvironmentManifestExample.ts
T
Edgar Arakelyan 291059adb4 feat(analysis-view): project an environment manifest from web network captures (#4954)
* feat(analysis-view): project an environment manifest from web network captures

Add the environment-manifest view kind to inspect_analysis_view for retained
HAR Evidence: the primary origin's recorded request lines become a
deterministic tool surface (names, path templates, parameters with their
rule and basis, observed query names, value-free body shapes, record
pointers), with excluded groups, three fixed unknowns and the shared
projection budget. The four format rules always apply; after-uid-literal,
opaque-token and sibling-variance are opt-in and labelled, and literal
fold-in never touches a format-rule parameter. The api-surface accumulator
moves onto shared route, media-type and budget helpers without behaviour
change.

Refs morluto/rea#4933

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test(web-network): check the manifest refusal text like the api-surface refusal

The real-capture verifier stringified the whole MCP result for the
environment-manifest refusal, so a declared "[" literal matched the result
envelope itself; compare the parsed refusal as the api-surface branch does.

Refs morluto/rea#4933

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs(specs): record the hosted web-network lane run for the manifest view

Refs morluto/rea#4933

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs(specs): reconcile the manifest view verification scope note with the hosted run

Refs morluto/rea#4933

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(analysis-view): bound body, route and name work in the environment manifest

Addresses the automated review findings on the PR: read request bodies at the
top level only instead of walking nested values; admit every literal route
part before materializing it; compare at most 32 segments in sibling-variance
and say so; keep stop words literal after a uid literal; keep the next
collision suffix per tool-name base; preserve a templated exclusion when two
groups print the same row.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(analysis-view): keep manifest folding, naming and media-type reads linear

Addresses the second automated review round: fold literal siblings through
set membership, key collision suffixes by the truncated name they share, and
charge a request media type before it is scanned.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(analysis-view): charge the manifest group key before building it

Addresses the third automated review round: the composite key copies the
method, origin, template and rules, so that copy is reserved before the key
is stringified.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(analysis-view): accept a +json suffix only on application media types

Addresses the fourth automated review round: `image/example+json` or
`text/example+json` no longer counts as JSON for origin selection, exclusion
or body shapes, matching the documented `application/json` and
`application/*+json` contract.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(analysis-view): require the capture parent's exact subject for a manifest

Addresses the fifth automated review round: the manifest cites the report
path and digest, so a parent whose subject is absent or names another path or
SHA-256 is refused at admission, as the capture view already does; the
api-surface view keeps its existing admission.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(analysis-view): share truncated suffix allocation work

Track suffix progress by the actual truncated prefix and suffix width. Distinct full names can collide only after suffix truncation; sharing that progress prevents quadratic rescanning while preserving shorter free names and existing allocation order.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* fix(analysis-view): merge only identifier-like siblings under sibling-variance

Addresses the sixth automated review finding: a bucket merged whenever one
literal carried a digit, folding plain words such as `mine` into the
parameter. The rule now partitions the bucket, merges the identifier-like
siblings and leaves the other literals as their own tools.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(analysis-view): combine qualifying sibling cohorts

Keep coded identifiers and a qualifying plural hyphenated cohort in one eligibility Set before checking the minimum merge size. Read each literal once and reuse the same membership at the consumer, preserving non-identifier routes and the three-hyphen minimum.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* docs(analysis-view): clarify opt-in sibling folding

State the additional sibling-variance selection in the guide, acceptance specification and MCP parameter description. Preserve the existing single-rule behavior and caller-selected inference rules.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-19 07:07:37 +08:00

133 lines
4.5 KiB
TypeScript

/** Generated from three environmentManifestHar records (dashboard read, folder create, avatar asset) through the HAR decoder and view service. */
export const ENVIRONMENT_MANIFEST_EXAMPLE = {
input: {
source: {
kind: "retained-evidence",
evidence_id:
"ev_3a9d300b14dc39c182f1ad6438e84870eed21da719c550bb9661958f69ce6277",
},
view: {
kind: "environment-manifest",
// Contract examples carry mutable JSON values; the tuple stays literal-typed.
template_rules: ["after-uid-literal"] as ["after-uid-literal"],
},
},
output: {
parent_evidence_id:
"ev_3a9d300b14dc39c182f1ad6438e84870eed21da719c550bb9661958f69ce6277",
parent_operation: "inspect_web_network_capture",
parent_digest:
"3a9d300b14dc39c182f1ad6438e84870eed21da719c550bb9661958f69ce6277",
view_digest:
"c5c7ea379b9ec8cbb1b6f6d2a2b37d251782d68ce3610166cb7112f830b9c6c3",
artifact: {
path: "/fixtures/environment-manifest.har",
sha256:
"5bee3e93d69fa5f8650737417615da7fda09f1d8f3e39c1fae8ee69fbcb03420",
},
coverage: {
status: "complete-within-view",
examined: 2,
total: 2,
next_offset: null,
exhausted: true,
},
limitations: [
"Body shapes list top-level JSON key types from one recorded request each; they do not establish required fields or a complete schema.",
"HAR text is producer-decoded Unicode. Original body bytes remain unknown unless content explicitly uses valid base64; declared sizes are preserved independently of retained bytes. mitmproxy 12.2.3 SaveHar may report null optional postData.text; that null is retained and omitted only from the upstream validation copy.",
"Observed query parameter names do not establish required parameters, value types or a complete API schema.",
"Opt-in template rules after-uid-literal merge recorded literals by shape; merged parameters may conflate distinct resources.",
"Tool templates, parameters and names are inferences from recorded request lines; they do not establish handler identity, URL-space schemas or causality.",
],
unknowns: [
{
kind: "authorization-model",
statement:
"Authorization headers and cookies are structurally excluded from captures; the manifest states no authorization model.",
},
{
kind: "response-semantics",
statement:
"Recorded statuses and bodies are retained only as pointers to parent records; the manifest states no response semantics.",
},
{
kind: "state-transition",
statement:
"Recorded exchanges are independent observations; the manifest states no ordering, side effects or state transitions between tools.",
},
],
origin: "http://grafana.example",
name: "grafana-example",
tools: [
{
name: "get_dashboards_by_uid",
method: "GET",
path_template: "/api/dashboards/uid/{uid}",
api_surface_template: "/api/dashboards/uid/acb4v6",
params: [
{
index: 3,
name: "uid",
rule: "after-uid-literal",
basis: "inferred",
observed_values: ["acb4v6"],
},
],
query_parameter_names: [],
body_shape: null,
statuses: [200],
content_types: ["application/json"],
record_ordinals: [0],
},
{
name: "post_folders",
method: "POST",
path_template: "/api/folders",
api_surface_template: "/api/folders",
params: [],
query_parameter_names: [],
body_shape: {
type: "object",
properties: {
title: "string",
},
},
statuses: [200],
content_types: ["application/json"],
record_ordinals: [1],
},
],
records: [
{
ordinal: 0,
method: "GET",
path: "/api/dashboards/uid/acb4v6",
status: 200,
content_type: "application/json",
},
{
ordinal: 1,
method: "POST",
path: "/api/folders",
status: 200,
content_type: "application/json",
},
],
excluded: [
{
method: "GET",
origin: "http://grafana.example",
path_template: "/avatar/{hex}",
reason: "ui-page-or-asset",
},
],
templated_groups: 2,
kind: "environment-manifest",
view: {
kind: "environment-manifest",
template_rules: ["after-uid-literal"],
include_query_parameter_names: true,
},
},
} as const;