Files
reverse-engineering-anything/src/domain/analysisView/environmentStateView.ts
T
Edgar Arakelyan 957bcfa149 feat(environment): preserve view-specific written clock spellings
Share clock rendering between analysis and local serving. Preserve exact sub-millisecond facts, bound learned format state per field, and charge retained output to the projection budget.

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-23 00:12:26 +08:00

418 lines
14 KiB
TypeScript

import { z } from "zod";
import type { AnalysisError } from "../analysisErrorBase.js";
import { jsonValueSchema } from "../jsonValue.js";
import { ok, type Result } from "../result.js";
import type {
AnalysisViewParent,
AnalysisViewRequest,
UnsignedAnalysisView,
} from "./analysisView.js";
import { EnvironmentStateAccumulator } from "./EnvironmentStateAccumulator.js";
import {
groupCaptureRecords,
templateRulesSchema,
} from "./environmentManifestView.js";
export const environmentStateViewRequestSchema = z.strictObject({
kind: z.literal("environment-state"),
template_rules: templateRulesSchema.describe(
"Opt-in template rules as for environment-manifest, each listed at most once. Use the rules of the manifest the state accompanies so tool names match.",
),
});
const jsonObjectFieldsSchema = z.record(z.string(), jsonValueSchema);
const queryParameterSchema = z.discriminatedUnion("role", [
z.strictObject({
role: z.literal("filter"),
field: z
.string()
.describe(
"Entities whose field equals the value as text, or whose array field holds it directly or as an object's id, slug or name.",
),
}),
z.strictObject({
role: z.literal("search"),
fields: z
.array(z.string())
.describe(
"Entities with a string field containing a plain substring value, ignoring case; an empty value matches every entity. Syntax-like operators replay instead.",
),
}),
z
.strictObject({ role: z.literal("sort") })
.describe(
"Orders by the field the value names (`name`, `-name`, `name:desc`, `name-ASC`): numbers by value, text ignoring case, null last ascending.",
),
z
.strictObject({ role: z.literal("direction") })
.describe("asc or desc for the sort parameter."),
z
.strictObject({ role: z.literal("limit") })
.describe("Page size; zero or a negative value lists every match."),
z
.strictObject({ role: z.literal("offset") })
.describe("Matches skipped before the page."),
z.strictObject({
role: z.literal("page"),
first: z
.number()
.int()
.describe(
"The first page, 0 or 1, when recordings distinguish the convention.",
),
}),
z.strictObject({
role: z.literal("inert"),
values: z
.array(z.string())
.describe(
"Recorded values that change nothing the view lists; other values are not computed.",
),
}),
]);
const listQuerySchema = z.strictObject({
parameters: z
.record(z.string(), queryParameterSchema)
.describe(
"What each query parameter does. A call whose every parameter has a role is answered by evaluating them over the state.",
),
record_ordinals: z
.array(z.number().int().nonnegative())
.describe("Checked lines the query reproduces, answered by evaluating it."),
totals: z
.array(z.string())
.describe(
"Observed envelope members with reproducible counts of every match before paging; absent or unknown representations stay recorded.",
),
});
const checkedSchema = z
.array(z.number().int().nonnegative())
.describe(
"Latest init record per request line that the view reproduced from the state.",
);
const clockFormatSchema = z.discriminatedUnion("kind", [
z.strictObject({
kind: z.literal("iso"),
separator: z.enum(["T", " "]),
fraction: z.number().int().nonnegative().max(9),
zone: z.string(),
}),
z.strictObject({ kind: z.literal("epoch"), unit: z.enum(["s", "ms"]) }),
]);
const entityViewFields = {
tool: z.string(),
type: z.string(),
pointer: z
.string()
.describe(
"JSON pointer to the entity object or array within the response: the root or one member, or for an item the element of a one-element array there (/0 or /<member>/0).",
),
fields: z
.array(z.string())
.describe("Ordered fields the view shows of each entity."),
required_fields: z.array(z.string()),
checked_record_ordinals: checkedSchema,
clock_formats: z
.record(z.string(), clockFormatSchema)
.optional()
.describe(
"How the view spells clock fields it always spells otherwise than the entities' stored values; a stored value the view never recorded is spelled this way.",
),
};
const viewSchema = z.discriminatedUnion("shape", [
z.strictObject({
...entityViewFields,
shape: z.literal("item"),
envelope_ordinal: z
.number()
.int()
.nonnegative()
.describe(
"Record whose response, with the entity object replaced, every item response shares.",
),
projected_record_ordinals: z
.array(z.number().int().nonnegative())
.optional()
.describe(
"Recorded reads that asked for some fields only (`fields`, `select`) and did not return the item; calls naming a projection replay.",
),
}),
z.strictObject({
...entityViewFields,
shape: z.literal("list"),
membership: z
.enum(["all", "recorded"])
.describe(
"all: every entity of the collection in state order; recorded: the entities each recorded response listed, except on the lines all_record_ordinals names.",
),
all_record_ordinals: z
.array(z.number().int().nonnegative())
.optional()
.describe(
"With recorded membership, the checked lines that each listed every entity of their collection in state order, with only paging parameters starting at zero; a call answered on one lists the whole collection. Absent when none does.",
),
counters: z
.array(z.string())
.describe("Envelope members that count the listed entities."),
query: listQuerySchema
.optional()
.describe(
"How the view's other checked lines select and order entities, when filters, a search, a sort or paging explain them; absent when none does.",
),
}),
z.strictObject({
tool: z.string(),
shape: z.literal("document"),
checked_record_ordinals: checkedSchema,
}),
z.strictObject({
tool: z.string(),
shape: z.literal("recorded"),
reason: z.enum([
"query-body",
"no-init-recording",
"body-unusable",
"no-identity",
"path-varies",
"query-varies",
"fields-differ",
"envelope-varies",
"list-differs",
"rendered-oversize",
]),
}),
]);
const ordinalSchema = z.number().int().nonnegative();
const ruled = { field: z.string() };
const fieldRuleSchema = z.discriminatedUnion("rule", [
z.strictObject({
...ruled,
rule: z.literal("identity"),
generator: z.enum(["sequence", "uuid", "token"]),
}),
z.strictObject({
...ruled,
rule: z.literal("sequence"),
scope: z.enum(["collection", "type"]),
}),
z.strictObject({ ...ruled, rule: z.literal("unique") }),
z.strictObject({
...ruled,
rule: z.literal("renewed"),
pointers: z.array(z.string()).min(1),
}),
z.strictObject({
...ruled,
rule: z.literal("clock"),
format: clockFormatSchema,
}),
z.strictObject({ ...ruled, rule: z.literal("echo") }),
z.strictObject({ ...ruled, rule: z.literal("copy"), from: z.string() }),
z.strictObject({ ...ruled, rule: z.literal("path"), from: z.string() }),
z.strictObject({ ...ruled, rule: z.literal("slug"), from: z.string() }),
z.strictObject({ ...ruled, rule: z.literal("template"), from: z.string() }),
z.strictObject({ ...ruled, rule: z.literal("increment") }),
z.strictObject({
...ruled,
rule: z.literal("constant"),
value: jsonValueSchema,
}),
]);
const insertRuleSchema = z.union([
z.strictObject({ rule: z.enum(["first", "last"]) }),
z.strictObject({
rule: z.literal("sorted"),
field: z.string(),
direction: z.enum(["ascending", "descending"]),
}),
]);
const writePlanSchema = z.strictObject({
tool: z.string(),
effect: z.enum(["create", "update", "delete", "document", "none"]),
target: z
.string()
.nullable()
.describe(
"The entity type an entity effect writes, or the document view a document write changes.",
),
served: z.boolean(),
reason: z
.enum([
"no-state-target",
"no-call",
"body-unusable",
"no-observation",
"unexplained-change",
"contradicted",
"side-effect",
"unconfirmed",
"incomplete-entities",
])
.nullable(),
exemplar_ordinal: ordinalSchema
.nullable()
.describe("The first successful recorded call the plan was built from."),
observation: z
.strictObject({ ordinal: ordinalSchema, pointer: z.string() })
.nullable()
.describe(
"The record whose response showed the written entity or document, and a JSON pointer to it.",
),
response: z
.strictObject({
ordinal: ordinalSchema,
shape: z.enum(["entity", "identity", "empty", "recorded"]),
pointer: z.string().nullable(),
})
.nullable(),
body: z
.strictObject({
accepted: z.array(z.string()),
fixed: z.array(
z.strictObject({ member: z.string(), sent: jsonValueSchema }),
),
required: z.array(z.string()),
identity: z.string().nullable(),
kinds: z.array(
z.strictObject({
member: z.string(),
kind: z.enum(["array", "boolean", "number", "object", "string"]),
}),
),
})
.nullable()
.describe(
"Body members the application stored as sent (accepted), members accepted only with their recorded value (fixed), members every call sends (required), and the JSON type a later call stored an accepted member as when the observation does not show it (kinds).",
),
rules: z
.array(fieldRuleSchema)
.describe(
"How written fields get their values; unlisted fields of a created entity keep the observed value.",
),
collection: z
.string()
.nullable()
.describe(
"For a create whose recorded values are tied to its collection, the only collection it writes to.",
),
insert: insertRuleSchema.nullable(),
merge: z.enum(["merge", "replace"]).nullable(),
checked_record_ordinals: z
.array(ordinalSchema)
.describe("Later reads that reproduced from the state after this write."),
contradicted_by: ordinalSchema.nullable(),
});
export const environmentStateFields = {
origin: z
.string()
.describe("Primary origin, as the environment manifest selects it."),
name: z.string(),
timeline: z
.enum(["recorded-start-time", "record-order"])
.describe(
"Records are ordered by recorded start time, or by record order when any start time is missing.",
),
types: z.array(
z.strictObject({
collection_template: z.string(),
id_field: z.string(),
id_basis: z
.enum(["path-parameter", "field-name"])
.describe(
"path-parameter: item responses repeat the requested path value in this field; field-name: inferred from listed items as the first identity name, else the first field ending in Id or _id shown distinct across at least two items, that every listed item carries distinctly.",
),
init_before_ordinal: z
.number()
.int()
.nonnegative()
.nullable()
.describe(
"First recorded write whose path root relates to the type; state comes from records that finished before it.",
),
volatile_fields: z
.array(z.string())
.describe(
"Fields whose recorded values changed with no recorded write between; the state keeps the latest.",
),
}),
),
entities: z
.array(
z.strictObject({
type: z.string(),
collection: z.string(),
id: z.string(),
fields: jsonObjectFieldsSchema,
view_fields: z.array(
z.strictObject({ tool: z.string(), fields: jsonObjectFieldsSchema }),
),
record_ordinals: z.array(z.number().int().nonnegative()),
}),
)
.describe(
"Entities in collection order: type, collection, then first sighting.",
),
documents: z.array(
z.strictObject({
tool: z.string(),
path: z.string(),
value: jsonValueSchema,
record_ordinal: z.number().int().nonnegative(),
}),
),
views: z.array(viewSchema),
writes: z
.array(writePlanSchema)
.describe(
"One plan per tool whose method may change state: its modelled effect, and whether the capture's later reads confirmed it (served).",
),
write_clock: z
.string()
.nullable()
.describe(
"The instant a served clock starts from: the end of the capture's last timed record.",
),
unknowns: z.array(
z.strictObject({
kind: z.enum([
"authorization-model",
"response-semantics",
"state-transition",
]),
statement: z.string(),
}),
),
};
/** Extract the init state a capture's reads observed and each read tool's render plan; types and identity are inferences. */
export const projectEnvironmentStateView = (
parent: AnalysisViewParent,
view: Extract<AnalysisViewRequest, { kind: "environment-state" }>,
accumulator = new EnvironmentStateAccumulator(undefined, view.template_rules),
): Result<UnsignedAnalysisView, AnalysisError> => {
const grouped = groupCaptureRecords(parent, view.kind, view.template_rules, {
accumulator,
noun: "state",
limitations: [
"Entity types, identity fields, list membership and counters are inferences from recorded response bodies; the state does not establish server-side schemas, derived values or values copied outside entity objects.",
],
});
if (!grouped.ok) return grouped;
const state = accumulator.finishState();
if (!state.ok) return state;
return ok({
kind: "environment-state",
view,
parent_evidence_id: parent.evidenceId,
parent_operation: grouped.value.operation,
parent_digest: parent.evidenceId.slice(3),
artifact: {
path: grouped.value.artifact.path,
sha256: grouped.value.artifact.sha256,
},
...state.value,
});
};