@intentius/chant-lexicon-cedar 0.44.9 → 0.44.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -2
- package/dist/agentcore/embed.d.ts +189 -0
- package/dist/agentcore/embed.d.ts.map +1 -0
- package/dist/agentcore/enforcement.d.ts +76 -0
- package/dist/agentcore/enforcement.d.ts.map +1 -0
- package/dist/agentcore/scan.d.ts +46 -0
- package/dist/agentcore/scan.d.ts.map +1 -0
- package/dist/avp/client.d.ts +85 -8
- package/dist/avp/client.d.ts.map +1 -1
- package/dist/codegen/docs-dogwood.d.ts +21 -0
- package/dist/codegen/docs-dogwood.d.ts.map +1 -0
- package/dist/codegen/docs.d.ts.map +1 -1
- package/dist/dogwood/cli.d.ts +41 -0
- package/dist/dogwood/cli.d.ts.map +1 -1
- package/dist/dogwood/index.d.ts +10 -4
- package/dist/dogwood/index.d.ts.map +1 -1
- package/dist/dogwood/replay-activity.d.ts +196 -0
- package/dist/dogwood/replay-activity.d.ts.map +1 -0
- package/dist/dogwood/replay-op.d.ts +165 -0
- package/dist/dogwood/replay-op.d.ts.map +1 -0
- package/dist/dogwood/serialize.d.ts +20 -0
- package/dist/dogwood/serialize.d.ts.map +1 -1
- package/dist/dogwood/trace.d.ts +215 -0
- package/dist/dogwood/trace.d.ts.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/integrity.json +5 -3
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/post-synth/dwdc013.d.ts +33 -0
- package/dist/lint/post-synth/dwdc013.d.ts.map +1 -0
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/okf/index.md +1 -0
- package/dist/okf/rules/DWDC013.md +15 -0
- package/dist/okf/types/Policy.md +1 -0
- package/dist/op/activities/index.d.ts +18 -0
- package/dist/op/activities/index.d.ts.map +1 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/rules/dwdc013.ts +64 -0
- package/dist/skills/chant-cedar-dogwood.md +327 -0
- package/package.json +7 -2
- package/src/agentcore/embed.test.ts +254 -0
- package/src/agentcore/embed.ts +399 -0
- package/src/agentcore/enforcement.test.ts +43 -0
- package/src/agentcore/enforcement.ts +92 -0
- package/src/agentcore/scan.ts +119 -0
- package/src/avp/OWNERSHIP.md +38 -0
- package/src/avp/client.test.ts +271 -0
- package/src/avp/client.ts +150 -16
- package/src/codegen/docs-dogwood.ts +1119 -0
- package/src/codegen/docs.ts +66 -1
- package/src/dogwood/cli.test.ts +122 -1
- package/src/dogwood/cli.ts +122 -1
- package/src/dogwood/index.ts +74 -1
- package/src/dogwood/replay-activity.test.ts +481 -0
- package/src/dogwood/replay-activity.ts +506 -0
- package/src/dogwood/replay-op.ts +242 -0
- package/src/dogwood/serialize.ts +37 -0
- package/src/dogwood/trace.test.ts +231 -0
- package/src/dogwood/trace.ts +471 -0
- package/src/index.ts +52 -0
- package/src/lint/audit-catalog.ts +8 -0
- package/src/lint/post-synth/dwd-post-synth.test.ts +119 -1
- package/src/lint/post-synth/dwdc013.ts +64 -0
- package/src/lint/post-synth/dwde-post-synth.test.ts +1 -1
- package/src/lint/post-synth/index.ts +2 -0
- package/src/op/activities/index.ts +27 -0
- package/src/plugin.test.ts +3 -2
- package/src/plugin.ts +30 -0
- package/src/skills/chant-cedar-dogwood.md +327 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dogwood dialect's docs pages (#1662, epic #1646).
|
|
3
|
+
*
|
|
4
|
+
* Separate from `./docs.ts` because the dialect is a surface inside this
|
|
5
|
+
* lexicon rather than a lexicon of its own: its pages sit in one sidebar group
|
|
6
|
+
* and get written and reviewed together, while `docs.ts` stays the cedar
|
|
7
|
+
* site's own shape. They are wired in as `extraPages` with `sidebar: false`
|
|
8
|
+
* plus a `sidebarExtra` group, so every page is reachable — Starlight does not
|
|
9
|
+
* auto-discover, and a page no sidebar entry points at exists only for whoever
|
|
10
|
+
* types the URL (#1312).
|
|
11
|
+
*
|
|
12
|
+
* The facts here come from the #1657 upstream verification and from this
|
|
13
|
+
* lexicon's own `src/dogwood/`. Where the two ever disagree the code wins: the
|
|
14
|
+
* builders are what a reader will actually run.
|
|
15
|
+
*/
|
|
16
|
+
export declare const dogwoodOverview = "[Dogwood](https://github.com/dogwood-policy/dogwood) is Cedar with temporal\noperators. A policy can ask what already happened in a session \u2014 was there a\nlogin in the last hour, how much has been transferred in the last fifteen\nminutes, has anything touched a classified document since the session started \u2014\nso approval-before-action, rate limits and budgets become policy instead of\napplication code.\n\nA `.dw` file is a Cedar policy with extra clause forms. Its head is Cedar's,\nbyte for byte, and its action schema is an ordinary `.cedarschema`.\n\n```\n@id(\"read_after_login\")\npermit (\n principal,\n action == Drupe::Action::\"Read\",\n resource\n)\nwhen temporal {\n formerly within 1h Drupe::Action::\"Login\"::response{ input.user: context.input.user }\n};\n```\n\n## Pre-release, and what that means here\n\nUpstream calls itself a reference interpreter and says, in bold on its own\nREADME, that it is **not intended for production use**. The gaps it enumerates:\nno event timestamp validation, no event authentication, no trace durability,\nunsandboxed Rhai in providers, no audit logging, no multi-tenancy isolation,\nand an `http_get` provider with no SSRF protection.\n\nMost of those are a runtime consumer's problem rather than chant's \u2014 chant's\nhalf is authoring, serialization and the walls, and evaluation stays with\nBedrock AgentCore Policy or whatever engine reads the emitted files. The part\nthat *is* chant's problem is that the language surface can move underneath the\ntyped builders, which is the next section.\n\n## How upstream is governed\n\nThere is no versioning story, and that is a finding rather than a complaint.\n\n| Question | Answer at the pinned revision |\n|---|---|\n| Tags | None |\n| GitHub releases | None |\n| Changelog | None |\n| Crate version | `1.0.0`, declared `publish = [\"brazil\"]` \u2014 an Amazon-internal registry, not crates.io |\n| Contributions | CONTRIBUTING declares the repo a read-only mirror, not accepting external PRs, not using GitHub issues |\n| Stability statement | Nowhere in README, CONTRIBUTING, SECURITY or the guide |\n\nEvery content change arrives as one squashed `Sync from internal source`\ncommit from a publish bot, authored against a repository nobody outside Amazon\ncan see. Over the repo's public life the cadence has been roughly one sync\nevery three days. A sync is a wholesale tree replacement, so it can retune the\ngrammar, rename a JSON field or swap the default macro library in a single\ncommit, and the crate will report `1.0.0` either way.\n\nSo a chant version gate cannot key off anything upstream publishes. What\n`src/dogwood/upstream.ts` records instead is a git SHA plus the blob hashes of\nseven files \u2014 three `.pest` grammars, the default macro library, and the three\n`dogwood-cli/src` files whose report structs are the JSON contract. The whole\ntree hash moves on docs-only syncs, which makes it too noisy to gate on.\n\nThree consequences run through everything else on these pages:\n\n- The typed builders target the **parser primitives**, never the named\n aggregates, because the aggregates live in a file a sync can edit and a\n caller can replace. See [Temporal Policies](../dogwood-temporal-policies/).\n- The CLI's **JSON report structs** are the integration surface, never its\n human text, because the human renderer is the likelier thing to get\n cosmetically retuned. See [Validation](../dogwood-validation/).\n- **Nothing in gating CI runs the binary.** Full `.dw` validation is a\n CLI-gated check that says out loud when it did not run.\n\n## A dialect, not a sibling lexicon\n\nk3s beside k3d and forgejo beside github are parallel peers with separate\nupstreams. Dogwood is not a peer: it embeds Cedar, a `.dw` file stripped of\nCedar semantics is meaningless, and the expensive machinery \u2014 schema codegen,\ntyped entity and action classes, meta-policy lint \u2014 is shared verbatim. It\nships as a surface inside this lexicon, with its checks under the `DWD` id\nfamily declared on the serializer's `extraRulePrefixes`.\n\n## Quick start\n\n```typescript\nimport { TemporalPolicy, TemporalEventSchema, dogwood } from \"@intentius/chant-lexicon-cedar\";\n\nexport const events = new TemporalEventSchema({ schema: dogwood.defaultEventSchema() });\n\nexport const readAfterLogin = new TemporalPolicy({\n annotations: { id: \"read_after_login\" },\n action: { eq: 'Drupe::Action::\"Read\"' },\n whenTemporal: [\n dogwood.formerly(\n \"1h\",\n dogwood.predicate('Drupe::Action::\"Login\"', \"response\", {\n \"input.user\": dogwood.ctx(\"input.user\"),\n }),\n ),\n ],\n});\n```\n\n## What comes out\n\n| File | What reads it |\n|---|---|\n| `policies.dw` | `dogwood validate` / `lower` / `replay` |\n| `events.dwschema` | `dogwood --event-schema` \u2014 the service half of the schema |\n| `macros.dw` | `dogwood --macros` \u2014 a macro library, when one is declared non-inline |\n| `<name>.cedar`, `policies.cedar.json` | The plain-Cedar half of the same policy set, unchanged |\n\nA build with no temporal policies emits none of the first three and behaves\nexactly as it did before. A build with both halves emits both from one pass,\nwith policy ids derived the same way on each leg.\n\n## Deploying it: AgentCore\n\n`AWS::BedrockAgentCore::Policy` \u2014 generated by the\n[aws lexicon](/chant/lexicons/aws/) \u2014 is where a temporal policy is actually\ndeployed. Its `Definition` is a two-arm `oneOf`: `Cedar.Statement` for plain\nCedar, `Policy.Statement` for anything else. The second arm is what a `.dw`\npolicy travels in, and it is why the epic picked AgentCore as the target.\n\n```typescript\nimport { agentCoreStagedPolicy } from \"@intentius/chant-lexicon-cedar\";\n\nnew BedrockAgentCorePolicy({\n PolicyEngineId: engine.ref(),\n ...agentCoreStagedPolicy(\"writeNeedsApproval\", writeNeedsApproval, \"log-only\"),\n});\n```\n\n`agentCorePolicyDefinition(name, policy)` picks the arm from the policy itself:\na `TemporalPolicy`, or any props carrying a temporal clause, goes to `Policy`;\nplain Cedar goes to `Cedar`. Nothing in the cedar lexicon imports the aws one \u2014\nthe seam is the data shape, the same rule the AVP embedding follows.\n\n`EnforcementMode` is the staging dial, and a temporal rule is the case that\nneeds it most: `LOG_ONLY` is evaluated on every request with its decision\nobserved rather than returned, so a policy whose behaviour depends on unreplayed\ntraffic can be watched before it binds. Promotion is one token, `\"log-only\"` to\n`\"enforce\"`.\n\nThe resource carries a statement and nothing else, so the event schema has\nnowhere to live in it and is registered with the engine separately. DWDC013\nwarns when a build embeds temporal text and emits no `.dwschema` beside it,\nbecause a deployed statement whose event kinds nobody registered matches\nnothing and stops doing its job without failing.\n\nThe worked example is `lexicons/cedar/examples/agentcore-policy`.\n\n## What chant does not do\n\nchant does not lower. `dogwood lower` compiles a `.dw` set to plain Cedar with\nthe temporal conditions hoisted into `context.*` slots; that is upstream's\nsemantics to own, and a reimplementation would drift the first time a sync\nchanged it. Where the lowered form is wanted, chant shells to the binary.\n\nchant does not evaluate at request time. Temporal decisions are made by the\npolicy engine in front of the traffic, and chant has no seat there. What chant\ndoes have is the offline half: `PolicyReplayOp` replays a declared set against\nrecorded history through upstream's own interpreter and reports where the\nverdicts diverged from what the policy set was supposed to decide.\n\n## The pages\n\n- [Temporal Policies](../dogwood-temporal-policies/) \u2014 the builders, the\n operators, and which of them are macros\n- [Event Schemas](../dogwood-event-schemas/) \u2014 the `.dwschema` surface and the\n `callerPrincipal` pin\n- [Validation](../dogwood-validation/) \u2014 which checks always run and which need\n the binary\n- [Replay](../dogwood-replay/) \u2014 typed traces, `PolicyReplayOp`, and the trap\n that makes half a trace pass silently\n";
|
|
17
|
+
export declare const dogwoodTemporalPolicies = "A `Dogwood::TemporalPolicy` is a `Cedar::Policy` with three extra clause\nforms. Upstream's policy grammar differs from Cedar's in exactly one rule:\n\n```\ncond = { cond_kw ~ (extension_marker | guardrails_tag? ~ \"{\" ~ expr ~ \"}\") }\n```\n\n| Prop | Emits | What it is |\n|---|---|---|\n| `when` / `unless` | `when { \u2026 }` | Ordinary Cedar expression strings, same as `Cedar::Policy` |\n| `whenGuardrails` / `unlessGuardrails` | `when guardrails { \u2026 }` | A Cedar expression with a tag upstream discards when lowering. It marks a clause for a reader; it does not change what the policy means |\n| `whenTemporal` / `unlessTemporal` | `when temporal { \u2026 }` | The temporal sub-language, dispatched to a different parser |\n\nClause order in the emitted file is fixed \u2014 every `when` form, then every\n`unless` form \u2014 rather than taken from the author. Conditions are a\nconjunction, so order carries no meaning, and fixing it means a policy that\ngains a temporal clause does not reshuffle the clauses already there.\n\n## The primitives\n\nThese seven are the whole temporal keyword set in upstream's\n`extension/temporal/grammar.pest`. Everything else you will read about dogwood\nis built out of them.\n\n| Builder | Renders | Notes |\n|---|---|---|\n| `formerly(w, \u03C6)` | `formerly within 1h \u03C6` | \u03C6 held at some point in the window |\n| `previous(w, \u03C6)` | `previous within 30s \u03C6` | \u03C6 held at the immediately preceding timepoint in the window |\n| `since(\u03C6, w, \u03C8)` | `\u03C6 since within 1h \u03C8` | Infix; \u03C6 has held continuously since \u03C8 |\n| `exists(binder, \u03C6)` | `exists (total: Long). \u03C6` | Binds a value for the body to compare |\n| `tp(binder)` | `tp(t)` | Binds the timepoint under evaluation |\n| `count(binders, \u03C6)` | `count for (t: Timepoint). where \u03C6` | The aggregation domain is mandatory |\n| `sum(over, binders, \u03C6)` | `sum a for (a: Long), (t: Timepoint). where \u03C6` | `over` names the summed variable |\n\nPlus `and`, `not`, `compare`, and `predicate` for an event head.\n\nTwo properties of the operator set are worth stating plainly. All of them are\n**past-only** \u2014 there is no future operator, and no way to write one. And\n`formerly`, `previous` and `since` all carry a **mandatory window**: an\ninteger and one of `s`, `m`, `h`, `d`, and nothing else. The builders take the\nwindow as an argument, so a windowless operator has nowhere to live; DWDC012\ncatches the ones that arrive by other routes.\n\n## Four policies\n\nApproval before action:\n\n```typescript\nimport { TemporalPolicy, dogwood } from \"@intentius/chant-lexicon-cedar\";\n\nexport const readAfterLogin = new TemporalPolicy({\n annotations: { id: \"read_after_login\" },\n action: { eq: 'Drupe::Action::\"Read\"' },\n whenTemporal: [\n dogwood.formerly(\n \"1h\",\n dogwood.predicate('Drupe::Action::\"Login\"', \"response\", {\n \"input.user\": dogwood.ctx(\"input.user\"),\n }),\n ),\n ],\n});\n```\n\nA rate limit, from the `count` primitive:\n\n```typescript\nexport const rateLimited = new TemporalPolicy({\n annotations: { id: \"rate_limited\" },\n action: { eq: 'Drupe::Action::\"Transfer\"' },\n whenTemporal: [\n dogwood.compare(\n dogwood.count(\n [dogwood.typedBinder(\"t\", \"Timepoint\")],\n dogwood.formerly(\n \"15m\",\n dogwood.and(dogwood.predicate('Drupe::Action::\"Transfer\"', \"request\"), dogwood.tp(\"t\")),\n ),\n ),\n \"<\",\n 5,\n ),\n ],\n});\n```\n\nA budget, from `sum` behind a macro, with `exists` naming the total:\n\n```typescript\nimport { TemporalMacroLibrary, TemporalPolicy, dogwood } from \"@intentius/chant-lexicon-cedar\";\n\nconst sumFormerly = dogwood.defTemporalMacro(\n \"sum_formerly\",\n [\"?a\", \"?w\", \"?body\"],\n dogwood.sum(\n \"?a\",\n [dogwood.typedBinder(\"?a\", \"Long\"), dogwood.typedBinder(\"$t\", \"Timepoint\")],\n dogwood.formerly(\n dogwood.macroWindow(\"?w\"),\n dogwood.and(dogwood.macroCondition(\"?body\"), dogwood.tp(\"$t\")),\n ),\n ),\n \"Sums the numeric value `?a` over occurrences of `?body` within window `?w`.\",\n);\n\nexport const library = new TemporalMacroLibrary({ macros: [sumFormerly], inline: true });\n\nexport const transferBudget = new TemporalPolicy({\n annotations: { id: \"transfer_sum_over_100\" },\n action: { eq: 'Drupe::Action::\"Alert\"' },\n whenTemporal: [\n dogwood.exists(\n dogwood.typedBinder(\"total\", \"Long\"),\n dogwood.and(\n dogwood.compare(\n dogwood.call(\"sum_formerly\", [\n dogwood.varRef(\"a\"),\n dogwood.interval(\"1h\"),\n dogwood.predicate('Drupe::Action::\"Transfer\"', \"request\", {\n \"input.user\": dogwood.varRef(\"_\"),\n \"input.amount\": dogwood.varRef(\"a\"),\n }),\n ]),\n \"==\",\n dogwood.varRef(\"total\"),\n ),\n dogwood.compare(dogwood.varRef(\"total\"), \">\", 100),\n ),\n ),\n ],\n});\n```\n\nSequencing, with a guardrail and a break-glass exemption:\n\n```typescript\nexport const noToolAfterSensitiveRead = new TemporalPolicy({\n effect: \"forbid\",\n annotations: { id: \"no_tool_after_sensitive_read\" },\n action: { eq: 'Drupe::Action::\"Invoke\"' },\n whenGuardrails: ['context.input.tool != \"audit\"'],\n whenTemporal: [\n dogwood.since(\n dogwood.predicate('Drupe::Action::\"Read\"', \"response\", {\n \"output.classification\": dogwood.varRef(\"c\"),\n }),\n \"30m\",\n dogwood.predicate('Drupe::Action::\"Login\"', \"request\"),\n ),\n ],\n unless: ['principal in Drupe::Group::\"breakglass\"'],\n});\n```\n\nThose four emit this, and the golden test in `src/dogwood/serialize.test.ts`\npins it byte for byte:\n\n```\n// Sums the numeric value `?a` over occurrences of `?body` within window `?w`.\ndef temporal sum_formerly(?a, ?w, ?body) {\n sum ?a for (?a: Long), ($t: Timepoint). where formerly within ?w (?body && tp($t))\n};\n\n@id(\"read_after_login\")\npermit (\n principal,\n action == Drupe::Action::\"Read\",\n resource\n)\nwhen temporal {\n formerly within 1h Drupe::Action::\"Login\"::response{ input.user: context.input.user }\n};\n\n@id(\"transfer_sum_over_100\")\npermit (\n principal,\n action == Drupe::Action::\"Alert\",\n resource\n)\nwhen temporal {\n exists (total: Long). (sum_formerly(a, 1h, Drupe::Action::\"Transfer\"::request{ input.user: _, input.amount: a })) == total && total > 100\n};\n\n@id(\"no_tool_after_sensitive_read\")\nforbid (\n principal,\n action == Drupe::Action::\"Invoke\",\n resource\n)\nwhen guardrails { context.input.tool != \"audit\" }\nwhen temporal {\n Drupe::Action::\"Read\"::response{ output.classification: c } since within 30m Drupe::Action::\"Login\"::request{}\n}\nunless { principal in Drupe::Group::\"breakglass\" };\n\n@id(\"rate_limited\")\npermit (\n principal,\n action == Drupe::Action::\"Transfer\",\n resource\n)\nwhen temporal {\n (count for (t: Timepoint). where formerly within 15m (Drupe::Action::\"Transfer\"::request{} && tp(t))) < 5\n};\n```\n\n## Primitives versus macros\n\nThis is the distinction to get right, and the reason the builder list above is\nshorter than most write-ups of dogwood.\n\n`count_within`, `sum_within` and `count_distinct_within` are **not**\noperators. They are macros defined in\n`dogwood-language/configuration/default_macros.dw`, alongside `bind`:\n\n```\ndef temporal count_within(?w, ?s) {\n count for ($t: Timepoint). where (formerly within ?w (?s && tp($t)))\n};\n```\n\n`once` is not even that. It appears in upstream's examples as an ordinary\nuser-defined macro and ships in no library at all. (The grammar rule behind\n`formerly` is internally named `once_op`, which is where the confusion\nstarts.) If you want `once`, define it \u2014 chant will not pretend it exists.\n\nA caller who passes `--macros` replaces the entire default library, so a\npolicy built on `count_within` is a policy built on an assumption about the\nfar end. chant therefore exposes the four as **calls**:\n\n```typescript\ndogwood.countWithin(\"15m\", dogwood.predicate('Drupe::Action::\"Transfer\"', \"request\"));\n// count_within(15m, Drupe::Action::\"Transfer\"::request{})\n```\n\nA call that resolves to nothing at the other end is a missing-macro error,\nwhich is comprehensible. A first-class builder emitting a name the callee's\nlibrary does not define would be a mystery.\n\nThe way to stop assuming is to emit the definitions yourself:\n\n```typescript\nimport { TemporalMacroLibrary, dogwood } from \"@intentius/chant-lexicon-cedar\";\n\nexport const macros = new TemporalMacroLibrary({ macros: dogwood.defaultMacroLibrary() });\n```\n\nThat writes `macros.dw` with upstream's four definitions verbatim. With\n`inline: true` they go at the top of `policies.dw` instead, and a policy set's\nown `def` shadows a same-named library macro \u2014 which makes inlining the\nstrongest form: the definitions travel with the policies and win over whatever\n`--macros` the caller supplies.\n\n## Writing macros\n\n```typescript\ndogwood.defTemporalMacro(\"once\", [\"?w\", \"?s\"], dogwood.formerly(dogwood.macroWindow(\"?w\"), dogwood.macroCondition(\"?s\")));\ndogwood.defCedarMacro(\"is_small\", [\"?n\"], \"?n < 100\");\n```\n\nTwo sigils, and they are not interchangeable:\n\n- `?p` splices the call-site argument literally. Build one with\n `macroWindow(\"?w\")` in window position, `macroCondition(\"?s\")` in condition\n position, `macroTerm(\"?a\")` in term position.\n- `$t` is a fresh binder the macro introduces, gensym'd at every expansion.\n\nBoth are legal only inside a macro body; upstream's well-formedness pass\nrejects them anywhere else, so the builders validate them at definition time.\n\nA call site supplies a window as a **bare interval** \u2014 `once(1h, \u2026)`, no\n`within` \u2014 because the keyword belongs to the operator and stays in the body.\nThat is `dogwood.interval(\"1h\")`.\n\n## Terms\n\nNumbers and booleans lift to literals. A bare string does not, and that is\ndeliberate: `\"alice\"` is a Cedar string literal and `alice` is a binder\nreference, and guessing which one was meant is how a policy silently stops\nmatching.\n\n| Builder | Renders |\n|---|---|\n| `str(\"alice\")` | `\"alice\"` |\n| `varRef(\"a\")` | `a` |\n| `ctx(\"input.user\")` | `context.input.user` |\n| `scopeRef(\"principal\", \"dept\")` | `principal.dept` |\n| `entityUid('Drupe::OAuthUser::\"alice\"')` | `Drupe::OAuthUser::\"alice\"` |\n| `decimalOf(\"1.50\")` | `decimal(\"1.50\")` |\n| `arrayOf(1, 2)` | `[1, 2]` |\n| `wildcard()` | `*` |\n\n## Precedence, and who handles it\n\nThe renderer parenthesises rather than relying on the reader knowing the\ngrammar. `!` binds tighter than `since` and `&&`, so `!a since within W b`\nnegates only `a`; an aggregate's `where` body is greedy, so\n`count for (\u2026). where \u03C6 == 3` would read `== 3` as part of \u03C6. Aggregates and\nmacro calls in comparison position are wrapped on both sides, and `exists`\nbinds maximally to the right so it is wrapped inside an `&&` chain.\n\n## The escape hatch\n\n`dogwood.raw(\"formerly within 1h \u2026\")` emits temporal source verbatim. It is\nthe one builder that can produce something the walls exist to catch, which is\nwhy the walls read the serialized text rather than the in-memory tree \u2014 see\n[Validation](../dogwood-validation/).\n\n## Next\n\n- [Event Schemas](../dogwood-event-schemas/) \u2014 what the `request`/`response`\n kinds in those predicates come from\n- [Validation](../dogwood-validation/) \u2014 what checks a policy set before it\n leaves the build\n";
|
|
18
|
+
export declare const dogwoodEventSchemas = "A dogwood policy set is checked against two schemas, and only one of them is\nrequired.\n\n| Half | Format | Flag | Required |\n|---|---|---|---|\n| Action schema | Cedar `.cedarschema` \u2014 entities, actions, each action's `context` | `--policy-schema` | Yes, for `validate`, `lower` and `replay` |\n| Service schema | `.dwschema` event DSL, a `providers.json`, a `.dw` macro library | `--event-schema`, `--providers`, `--macros` | No |\n\nThe action schema is the one the rest of this lexicon already generates from \u2014\nsee [Schema](../schema/). This page is about the other half.\n\nWith all three service flags omitted, upstream falls back to\n`ServiceSchema::defaults()`: `request` (deciding), `response` and `error`\nkinds, a universal `pin callerPrincipal = principal`, a 24h `max_window` cap,\nno providers, and the embedded default macro library.\n\n## The `.dwschema` surface\n\nThe grammar is 136 lines of pest and purely syntactic: an optional\n`max_window` directive, then a sequence of event declarations.\n\n```\nmax_window = 30d\n\ndecision event <A>::request {\n ...inputs(A),\n pin callerPrincipal: principalType(A) = principal,\n callerResource: resourceType(A),\n requestId: String,\n}\n```\n\n`A` is a symbolic action binder, not an action. **The file names no actions at\nall** \u2014 it says what shape an event of each kind has, for whichever action it\nis derived against. That is why DWDC010 can check a predicate's event *kind*\nagainst the emitted schema but not its action: the action half of that check\nlives in the `.cedarschema`, and it is the CLI's to make.\n\nEvent kind names are author-defined. `request`, `response` and `error` are\nconventional, not fixed.\n\n| Builder | Emits |\n|---|---|\n| `spreadInputs()` / `spreadOutputs()` | `...inputs(A)` / `...outputs(A)` |\n| `field(\"requestId\", concrete(\"String\"))` | `requestId: String` |\n| `field(\"callerResource\", resourceType())` | `callerResource: resourceType(A)` |\n| `field(\"meta\", record([\u2026]))` | a nested record, addressed as `meta.member` |\n| `pinnedField(name, type, pinPrincipal())` | `pin name: \u2026 = principal` |\n| `pinnedField(name, type, pinContext(\"input.user\"))` | `pin name: \u2026 = context.input.user` |\n\nA pinned field must be a leaf; upstream requires the `pin` prefix and the\n`= \u2026` clause together, and the builder enforces both rather than deferring to\nthe parser.\n\n## The default, and the pin\n\n```typescript\nimport { TemporalEventSchema, dogwood } from \"@intentius/chant-lexicon-cedar\";\n\nexport const events = new TemporalEventSchema({ schema: dogwood.defaultEventSchema() });\n```\n\nThat reproduces upstream's `pinned.dwschema` \u2014 the shape\n`ServiceSchema::defaults()` uses \u2014 and emits `events.dwschema`:\n\n```\n// The default event-schema shape: request/response/error, each correlated to\n// the deciding request's principal.\n\ndecision event <A>::request {\n ...inputs(A),\n pin callerPrincipal: principalType(A) = principal,\n callerResource: resourceType(A),\n requestId: String,\n sessionId: String,\n}\n\nevent <A>::response {\n ...inputs(A),\n ...outputs(A),\n pin callerPrincipal: principalType(A) = principal,\n callerResource: resourceType(A),\n requestId: String,\n sessionId: String,\n}\n\nevent <A>::error {\n ...inputs(A),\n pin callerPrincipal: principalType(A) = principal,\n callerResource: resourceType(A),\n requestId: String,\n sessionId: String,\n}\n```\n\n**The pin is the thing to understand before writing your own schema.**\n`pin callerPrincipal = principal` correlates every temporal predicate to the\ndeciding request's principal: events logged by other principals are invisible\nto `formerly`, `since` and every aggregate over them.\n\nSupplying *any* event schema opts out of upstream's default wholesale. So a\nschema emitted without a pin does not merely fail to add a correlation \u2014 it\nremoves one the policy author very likely assumed, and every predicate in the\nset starts matching other principals' events.\n\nThat is a legitimate design; cross-principal correlation is a reason to write\nyour own schema. It is also a decision, so chant makes it a named argument:\n\n```typescript\nexport const events = new TemporalEventSchema({\n schema: dogwood.defaultEventSchema({ pinCallerPrincipal: false }),\n});\n```\n\nwhich stamps the reasoning into the emitted file as a comment, and which\nDWDS010 reports as a warning in the build. Neither stops you. Both make the\nchoice visible in a diff.\n\n## `max_window`\n\nThe directive caps how far back any operator in the set may look. Absent, the\ncap is upstream's 24h default \u2014 the same 24h that applies when no schema is\nsupplied at all.\n\n```typescript\ndogwood.defaultEventSchema({ maxWindow: \"30d\" }); // max_window = 30d\n```\n\nDWDC011 does the arithmetic in TypeScript and fails the build on a window past\nthe cap, with no binary involved. Where several schemas are emitted the\ntightest cap wins, and macro-call intervals count: `once(48h, \u2026)` expands\nthrough `within ?w` and looks back exactly as far as `formerly within 48h`.\n\n## Several schemas\n\nOne `.dwschema` per file, because `max_window` is a single directive at the\ntop and concatenating two schemas would emit something upstream rejects. A\nbuild with more than one gives each an explicit filename:\n\n```typescript\nexport const gateway = new TemporalEventSchema({\n schema: dogwood.defaultEventSchema({ maxWindow: \"30d\" }),\n filename: \"gateway.dwschema\",\n});\n```\n\nTwo schemas targeting one filename is a serializer warning and only the first\nis written \u2014 a silent merge would produce a file that parses as neither.\n\n## Providers\n\nThe third service flag, `--providers`, takes a `providers.json` whose entries\ncarry `argumentTypes`, an `outputType` and an `implementation`. chant has no\ntyped builder for it today; the CLI adapter's bundle type accepts provider text\nif you assemble it, and the build's planner does not emit one.\n\nOne upstream trap worth recording even so: the CLI reads `--providers` as raw\ntext and **never resolves `scriptFile`**. Rhai has to be inlined under\n`implementation.script`, or `replay` fails per-evaluation with \"rhai\nimplementation has no script\" while `validate` and `lower` still pass.\n\n## Next\n\n- [Validation](../dogwood-validation/) \u2014 DWDC010, DWDC011 and DWDS010 in full\n- [Replay](../dogwood-replay/) \u2014 where the events these schemas describe\n actually come from\n";
|
|
19
|
+
export declare const dogwoodValidation = "Validation splits by what needs a binary, and the split is the point.\n\nEverything answerable in TypeScript runs on every build and gates. Full `.dw`\nvalidation needs upstream's own frontend, which ships as a Rust CLI and nothing\nelse \u2014 no npm package, no wasm build, no bindings \u2014 so it runs when the binary\nis there and says so out loud when it is not.\n\n| Check | Severity | Needs the binary |\n|---|---|---|\n| DWDC010 \u2014 a temporal predicate names a declared event kind | error | no |\n| DWDC011 \u2014 a window fits inside `max_window` | error | no |\n| DWDC012 \u2014 `formerly`/`previous`/`since` carries its window | error | no |\n| DWDC013 \u2014 an embedded AgentCore temporal statement has its event schema emitted | warning | no |\n| DWDS010 \u2014 an emitted event schema pins something | warning | no |\n| DWDE010 \u2014 the set validates clean under `dogwood validate` | error | yes |\n| DWDE011 \u2014 the lowered Cedar validates under `cedar-wasm` | error | yes |\n\nThe DWD family is an ordinary set of\n[post-synth checks](/chant/guide/organizational-policy/) under the prefix the\ncedar serializer declares in `extraRulePrefixes`. There is no second policy\nengine here; dogwood is a target, the same as Cedar.\n\nEvery one of them reads the **emitted text**, not the in-memory model, for the\nsame reason the CED checks read `policies.cedar.json`: `chant audit` runs over\na checked-in artifact chant did not write, and a wall that only fires on\nchant's own output is not a wall. It also keeps the builders and the walls\nindependent \u2014 DWDC012 catches a windowless `formerly` even though the builders\ncannot construct one, because `raw()` and a hand-written `.dw` both can.\n\n## The TypeScript walls\n\n**DWDC010** compares every predicate head in the temporal regions of a `.dw`\nfile against the event kinds the emitted `.dwschema` declares. Upstream rejects\nthe same thing with code `extension`. The check is silent when no `.dwschema`\nwas emitted: with none supplied, `ServiceSchema::defaults()` decides the kinds\nat the far end, and guessing that a project's out-of-band schema matches\nupstream's default would fail builds for a policy set that is fine.\n\n**DWDC011** fires with or without an emitted schema, because the cap applies\neither way \u2014 24h by default. See\n[Event Schemas](../dogwood-event-schemas/#max_window).\n\n**DWDC012** is the wall behind the typed builders. `formerly(w, body)` has\nnowhere to put a missing window, so the builders make it unrepresentable; the\ncheck is what covers `raw()`, hand-written files, and audits of trees chant\nnever wrote.\n\n**DWDC013** asks the question prior to DWDC010's, and only of statements that\nleft the `.dw` file behind. A policy embedded in `Definition.Policy.Statement`\ntravels as one string; the engine at the other end cannot match a temporal\npredicate until it knows what an event is. A build that embeds temporal text\nand emits no `.dwschema` has shipped half a policy \u2014 the statement deploys, the\npredicates match nothing, and a `formerly`-guarded forbid stops denying.\nWarning rather than error, because a project may register the service schema\nthrough a separate pipeline, and failing that build would be chant asserting a\nfact it cannot check.\n\n**DWDS010** is report-only. chant does not know whether cross-principal\ncorrelation was wanted, only that an unpinned schema should not slip through a\nreview unremarked.\n\nScanning is confined to the temporal regions of a file \u2014 every\n`temporal { \u2026 }` body and every `def temporal` body \u2014 so a Cedar attribute\nnamed `since` is not mistaken for the operator, and `context.retryWindow == 3`\nis not mistaken for a window.\n\n## The CLI-gated half\n\n**DWDE010** runs `dogwood validate --format json` over each emitted policy set\nand reports every finding. What it catches that the walls cannot: macro\nexpansion, the temporal type checker, and the Cedar body checked against the\naction schema through upstream's own frontend.\n\n**DWDE011** takes the `dogwood lower` output \u2014 plain Cedar with the temporal\nconditions hoisted into `context.*` slots, plus an augmented schema declaring\nthem \u2014 and runs the published `@cedar-policy/cedar-wasm` over it. That is a\ndifferent validator from the one vendored inside upstream, which makes a\nfinding here meaningful: it is drift between the Cedar upstream pins and the\nCedar the rest of chant validates against. The #1657 verification put all 86\nupstream example bundles through this exact path and every one validated clean\nin strict mode.\n\n### When the binary is absent\n\nOne `info` finding, naming the binary, where chant looked, and the issue. Not\nsilence. A check that quietly passes when it could not run is claiming a\nguarantee it never made.\n\n## Pointing chant at a binary\n\nThere is no published build. You build it from the pinned revision:\n\n```bash\ngit clone https://github.com/dogwood-policy/dogwood\ncd dogwood && git checkout 5063bcc2d6d6cf5024d1b0498e6cc8ef52cbcf0c\ncargo build --release\n```\n\nResolution order:\n\n1. An explicit `configureDogwoodCli({ binary })` call \u2014 taken as given, since\n its caller knows.\n2. `$CHANT_DOGWOOD_BINARY`.\n3. `cedar.dogwood.binary` in a `chant.config.json`, resolved by walking up\n from the working directory.\n4. `dogwood` on `PATH`.\n\n```bash\nexport CHANT_DOGWOOD_BINARY=/path/to/dogwood/target/release/dogwood\n```\n\n```json\n{ \"cedar\": { \"dogwood\": { \"binary\": \"./vendor/dogwood\" } } }\n```\n\nThe config knob reads `chant.config.json` only, and that is a real limitation\nrather than an oversight: a post-synth check's `check()` is synchronous, while\nchant's config loader is async and, under `chant build --sandbox`, evaluates a\n`chant.config.ts` in a child process. JSON is data, so reading it executes\nnothing. A project on `chant.config.ts` uses the environment variable or the\nprogrammatic override.\n\nA path from the environment or the config that is not executable is resolved\npast rather than returned to fail later, and the advisory names where chant\nlooked.\n\n## Why exit codes decide nothing\n\nThree properties of the CLI shape the adapter, all verified against the pinned\nsources.\n\n**Exit 2 is ambiguous.** It covers a rejected policy set *and* clap's own usage\nerror for an unknown flag \u2014 and upstream's published guide claims exit 1 for\nthe latter. Reading a bare non-zero exit as \"your policy is bad\" would fail a\nbuild over a flag rename in a sync nobody outside Amazon can review. So the\nadapter branches on the JSON on stdout, in both directions: a `passed: false`\nwith a zero exit is still a rejection, and the reverse is still a pass.\n\n**There are two JSON shapes.** A type-check finding arrives in a report \u2014\n`passed`, `passed_without_warnings`, `errors[]`, `warnings[]`. A fatal parse,\nmacro or lowering error replaces the whole report with a bare error object\ncarrying the same diagnostic fields at the top level plus `related[]`. Both\nnormalize into one diagnostic type, so nothing downstream has to know which\narrived.\n\n**A run that produced no usable JSON is neither a pass nor a rejection.** It is\nreported at `warning` severity as \"could not be validated\", and the policy set\nis explicitly described as neither accepted nor rejected.\n\nTwo smaller contract facts the adapter encodes: `--format json` writes to\nstdout for success and fatal alike, and `--emit` is ignored under\n`--format json` (the JSON always carries all three lowered artifacts), so it is\nnot passed.\n\nDiagnostic labels are **byte offsets** into the `.dw` source, and findings\nreport them as byte ranges. Converting to line and column would mean\nre-deriving line breaks over a file the adapter does not hold, and a wrong line\nnumber is worse than an honest offset.\n\n## Nothing in gating CI runs it\n\nBy design, from the epic: upstream instability is priced, not absorbed. The\nCLI-gated checks are for a developer with the binary and for on-demand\nharnesses in the `forgejo-runtime-e2e` shape. `PolicyReplayOp` shares that\nrule and the same binary discovery \u2014 see [Replay](../dogwood-replay/) \u2014 with\none difference: a replay step with no binary **fails**, where a build check\nwith no binary reports and moves on. A check that could not run should not\nblock a build; a replay that could not run has produced no answer at all.\n\n## Next\n\n- [Replay](../dogwood-replay/) \u2014 the third verb, wrapped as an activity and an\n Op rather than as a build check\n- [Lint Rules](../lint-rules/) \u2014 the cedar half of the same check set\n";
|
|
20
|
+
export declare const dogwoodReplay = "`dogwood replay` evaluates a policy set against a recorded event trace and\nreturns a verdict per decision point. It is how a temporal policy gets tested\nat all.\n\nA plain Cedar policy is decidable from its source: given the schema, a build\ncan say whether it parses, whether it type-checks, and what it applies to. The\nDWDC and CEDC checks already do that. A temporal policy is not decidable that\nway \u2014 whether `formerly within 1h Login::response{ \u2026 }` fires depends on a\nhistory nobody has replayed. So the check is a replay against recorded decision\nhistory, and the answer moves as the history does. That puts it on the observe\nend of the lifecycle dial, beside `WorkflowAuditOp`, with a finding mode as the\nreconcile step.\n\nThree pieces ship: a typed trace builder, a `dogwoodReplay` activity, and the\n`PolicyReplayOp` composite that pairs them. The worked example is\n`lexicons/cedar/examples/policy-replay`.\n\n## The Op\n\n```typescript\n// ops/policy-replay.op.ts\nimport { PolicyReplayOp } from \"@intentius/chant-lexicon-cedar\";\nimport { readAfterLoginExpectations } from \"../trace/read-after-login\";\n\nexport const { op } = PolicyReplayOp({\n name: \"policy-replay\",\n policiesPath: \"dist/policies.dw\",\n policySchemaPath: \"schema.cedarschema\",\n eventSchemaPath: \"dist/events.dwschema\",\n tracePath: \"trace/read-after-login.log\",\n expect: readAfterLoginExpectations,\n onFinding: \"report\",\n});\n\nexport default op;\n```\n\n```bash\nnpx chant run policy-replay\n```\n\nThree phases:\n\n| Phase | Step | What it does |\n|---|---|---|\n| Artifacts | `chantBuild` | Emits `policies.dw`, the `.cedarschema` and the `.dwschema` the replay reads. Pass `buildScript: false` when they are checked in and the phase is dropped rather than run empty |\n| Replay | `dogwoodReplay` | Runs `dogwood replay --format json` over the bundle and the trace, writes the divergence report |\n| Report | `dogwoodReplayReport` | Reads that report and acts on the finding mode |\n\nThe report file (`dist/dogwood-replay.json` by default) is the seam between the\nlast two phases, for the same reason `dist/fly.json` is the seam between\n`build:fly` and `flyApply`: Op steps do not hand return values to one another,\nso a phase boundary needs an artifact to be a real boundary.\n\nThe Replay step carries `outcomeAttribute: { name: \"Divergences\", from: \"findings\" }`,\nso \"show me the replays that found something\" is one filter rather than a log\nread. `onFinding` takes `report | issue | pull-request`; `report` prints the\nmarkdown, and the other two hand back a title and body for whatever opens them\n\u2014 the cedar lexicon has no forge client and does not grow one, the same\ndivision `workflowSupplyChainAudit` draws. `failOnDivergence` defaults to\nfalse: an observe-dial Op reports, and a red run is the caller's decision.\n\nThe composite ships from cedar, not from temporal, because it hands back an Op\nand nothing else. It imports `@intentius/chant/op` and carries no dependency on\nthe temporal lexicon. A project that wants it scheduled pairs it with a\n`TemporalSchedule` of its own \u2014 two lines, project-side, rather than a config\nflag that would drag the dependency in for everyone.\n\n## Typed traces\n\n`traceEvent()` builds one line. `renderTrace()` renders a list.\n`traceFixture()` does both and refuses to hand back a trace that would weaken\nits own replay.\n\n```typescript\nimport { dogwood } from \"@intentius/chant-lexicon-cedar\";\n\nconst { entityRef, traceEvent } = dogwood;\n\nconst ALICE = 'Drupe::OAuthUser::\"alice\"';\nconst GATEWAY = 'Drupe::Gateway::\"gw1\"';\n\nconst session = {\n scope: { principal: ALICE, resource: GATEWAY },\n context: { input: { user: \"alice\" } },\n} as const;\n\nconst injected = (requestId: string) => ({\n callerPrincipal: entityRef(ALICE),\n callerResource: entityRef(GATEWAY),\n requestId,\n});\n\nexport const trace = [\n traceEvent({ ...session, timestamp: 0, action: 'Drupe::Action::\"Login\"', record: injected(\"u1\") }),\n traceEvent({ ...session, timestamp: 0, action: 'Drupe::Action::\"Login\"', kind: \"response\", record: injected(\"u1\") }),\n traceEvent({ ...session, timestamp: 10, action: 'Drupe::Action::\"Read\"', record: injected(\"u2\") }),\n traceEvent({ ...session, timestamp: 7200, action: 'Drupe::Action::\"Read\"', record: injected(\"u3\") }),\n];\n```\n\n```\n@0 scope(principal: Drupe::OAuthUser::\"alice\", resource: Drupe::Gateway::\"gw1\") request_context(input: { user: \"alice\" }) Drupe::Action::\"Login\"::request(input: { user: \"alice\" }, callerPrincipal: Drupe::OAuthUser::\"alice\", callerResource: Drupe::Gateway::\"gw1\", requestId: \"u1\")\n```\n\nCompare the input to the output: `input` was written once, under `context`,\nand comes out in the `request_context` envelope **and** in the logged record.\n`kind` defaults to `request`. Values render in Cedar surface\nforms: strings quote themselves, `entityRef()` renders a uid bare,\n`decimalValue(\"1.50\")` keeps a scale a JS number would lose, and a non-integer\n`number` throws rather than emitting something the parser reads differently.\n\n## The both-bags trap\n\nEach line carries two field bags and they are not the same bag.\n\n```\n@10 \u2026 request_context(input: { user: \"alice\" }) Drupe::Action::\"Read\"::request(input: { user: \"alice\" }, callerPrincipal: \u2026)\n \u2514\u2500\u2500 the Cedar request is built from this \u2514\u2500\u2500 temporal predicates match against this\n```\n\n`formerly within 1h Drupe::Action::\"Login\"::response{ input.user: context.input.user }`\ncompares the *past* login's `input.user`, out of the logged record, against the\n*current* request's `context.input.user`, out of the `request_context`\nenvelope. Fill one bag and not the other and nothing errors: the replay exits 0\nwith a verdict that tested half of what it claims.\n\nSo the default is both bags, and the weaker trace takes an explicit opt-out:\n\n| `bags` | Where a `context` group lands |\n|---|---|\n| `\"both\"` (default) | `request_context` and the logged record |\n| `\"record-only\"` | The logged record alone \u2014 `context.*` is absent from the Cedar request |\n| `\"context-only\"` | The envelope alone \u2014 no temporal predicate can match the group |\n\n`record` is the other half of the input, and it is deliberately separate: the\nevent schema's own injections (`callerPrincipal`, `callerResource`,\n`requestId`, `sessionId`) belong to the logged record and are never part of the\nCedar request.\n\n## The action-naming trap\n\nAction names must be fully qualified \u2014 `Drupe::Action::\"Read\"`, never `Read`. A\nshort name leaves every temporal predicate unmatched while Cedar still\nauthorizes. `traceEvent()` rejects one at construction, as do `entityRef()` and\n`traceEntity()`.\n\n## Auditing a trace chant did not build\n\nA trace fetched from somewhere else \u2014 an AgentCore session history, a `.log`\nrecorded by hand \u2014 normalizes into the same `TraceEvent` list and takes the\nsame audit:\n\n```typescript\nconst issues = dogwood.auditTrace(events);\n```\n\n| Kind | What it means |\n|---|---|\n| `single-bag` | A group is in one bag and not the other, so one side of the check silently misses |\n| `no-request-context` | A deciding event has no envelope at all, so every `context.*` test misses |\n| `empty-record` | An event logs no fields, so no temporal predicate can match it |\n| `out-of-order` | A timestamp goes backwards; history accumulates in file order, so a window sees something different |\n\nEvery one of those makes a replay *weaker* rather than making it fail, which is\nthe class a green run hides. `decisionKinds` defaults to `[\"request\"]` \u2014 a\nhistory-only event never becomes a Cedar request, so a missing envelope on one\nis not a weakening and is not reported. The truth is whichever kinds the\nproject's `.dwschema` marks `decision`, and that file is not visible from the\naudit.\n\n`traceFixture(events)` runs the same audit and **throws** on any finding,\nnaming the `allow` list that would let it through. A fixture that weakens its\nown replay fails at build time instead of producing a green run that proves\nnothing.\n\n## Expectations\n\n```typescript\nexport const expectations = [\n { timestamp: 0, verdict: \"deny\", note: \"the login request itself is not permitted\" },\n { timestamp: 10, verdict: \"allow\", determiningRules: [0], note: \"the login is ten seconds old\" },\n { timestamp: 7200, verdict: \"deny\", note: \"the login is two hours stale\" },\n];\n```\n\nThree expectations for four trace lines, and that is the point of writing them\nagainst `timestamp` rather than `index`: `Login::response` is history-only\nunder the default event schema, so it contributes to the window and produces no\nverdict. `index` is the position in the *decision* stream, not the trace line\nnumber, and it shifts whenever a trace gains a history-only event.\n\n`determiningRules` is the second half of the assertion. A decision that comes\nout right for the wrong reason \u2014 the correct verdict carried by a different\nrule \u2014 is drift the verdict alone cannot show.\n\nWhat `compareVerdicts` reports: a verdict that differs from the expectation; a\nverdict that matches but was determined by different rules; a decision point an\nexpectation named that never occurred; a decision point that occurred and\nnothing expected. Per-evaluation errors are reported even when the expectation\nmatched, and \u2014 when no expectations were written at all \u2014 an errored evaluation\nis still a finding, because a provider with no inlined Rhai script would\notherwise replay \"clean\".\n\n## The trace format\n\nOne event per line. Blank lines are skipped, a leading BOM is stripped, and\nthere is **no comment syntax** \u2014 a `//` is an ordinary part of a value, so URLs\nsurvive and an attribution header would be parsed as an event and rejected with\n\"timepoint must start with `@`\".\n\n```\n@<timestamp> [scope(...)] [entities(...)] [request_context(...)] <Ns>::Action::\"<Name>\"::<kind>(<field>: <value>, ...)\n```\n\nThe timestamp is an `i64` after `@`. The three envelopes are optional and must\nappear in that order. Values use Cedar surface forms: entity refs, quoted\nstrings, integers, decimals like `1.50`, booleans, arrays, nested records.\n\n## Reading the run\n\nHuman output is one line per decision point:\n\n```\n@0 (time point 0): DENY\n@10 (time point 1): ALLOW [rules: 0]\n@7200 (time point 2): DENY\n```\n\nJSON gives `{verdicts: [{index, timestamp, verdict, determining_rules, errors}]}`.\nHistory-only events produce no line.\n\n**Replay exits 0 even when every verdict is DENY.** A non-zero exit means the\ntrace or the policy set failed to load, never that a policy denied. The adapter\nin `src/dogwood/cli.ts` reads the JSON and not the exit code, here as\neverywhere; an unrecognised verdict string is read as a deny rather than\ndropped, because dropping an entry would shift every later index.\n\n## It needs the binary, and does not pretend otherwise\n\nThe Replay phase shells to upstream's CLI. There is no npm package and no wasm\nbuild \u2014 see [Validation](../dogwood-validation/) for how chant finds a binary\nand how to build one.\n\nWithout one the step **fails** and says where chant looked. It does not degrade\nto a pass, and an unusable invocation or a fatal (a malformed trace line, an\nunparseable policy set) throws rather than reporting zero divergences. A replay\nthat did not happen is not a replay that found nothing \u2014 which is also why\nnothing in gating CI executes the binary.\n\n## Next\n\n- [Validation](../dogwood-validation/) \u2014 the two verbs that run inside a build,\n and the binary knobs replay shares\n- [The Dogwood Dialect](../dogwood/) \u2014 what pre-release means for all of this\n";
|
|
21
|
+
//# sourceMappingURL=docs-dogwood.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs-dogwood.d.ts","sourceRoot":"","sources":["../../src/codegen/docs-dogwood.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,eAAO,MAAM,eAAe,oiQA+K3B,CAAC;AAIF,eAAO,MAAM,uBAAuB,ghXA0TnC,CAAC;AAIF,eAAO,MAAM,mBAAmB,q6MAsK/B,CAAC;AAIF,eAAO,MAAM,iBAAiB,49QA8K7B,CAAC;AAIF,eAAO,MAAM,aAAa,inXA+PzB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../src/codegen/docs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;
|
|
1
|
+
{"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../src/codegen/docs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAqxBH;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA+HjF"}
|
package/dist/dogwood/cli.d.ts
CHANGED
|
@@ -167,6 +167,27 @@ export type DogwoodLowerResult = {
|
|
|
167
167
|
kind: "lowered";
|
|
168
168
|
value: DogwoodLowered;
|
|
169
169
|
} | DogwoodFatal | DogwoodUnusable;
|
|
170
|
+
/**
|
|
171
|
+
* One decision point out of `replay --format json`.
|
|
172
|
+
*
|
|
173
|
+
* `index` is the 0-based position in the *decision* stream, not the trace line
|
|
174
|
+
* number: a history-only event (any kind the event schema does not mark
|
|
175
|
+
* `decision`) contributes history and no verdict, so the two do not line up.
|
|
176
|
+
*/
|
|
177
|
+
export interface DogwoodVerdict {
|
|
178
|
+
index: number;
|
|
179
|
+
/** The `@<timestamp>` of the line that produced this decision. */
|
|
180
|
+
timestamp: number;
|
|
181
|
+
verdict: "allow" | "deny";
|
|
182
|
+
/** `.dw` rule indices that determined the decision — upstream's `[rules: 0, 2]`. */
|
|
183
|
+
determiningRules: number[];
|
|
184
|
+
/** Per-evaluation errors, such as a provider with no inlined Rhai script. */
|
|
185
|
+
errors: string[];
|
|
186
|
+
}
|
|
187
|
+
export type DogwoodReplayResult = {
|
|
188
|
+
kind: "replayed";
|
|
189
|
+
verdicts: DogwoodVerdict[];
|
|
190
|
+
} | DogwoodFatal | DogwoodUnusable;
|
|
170
191
|
/**
|
|
171
192
|
* Read a `validate --format json` run.
|
|
172
193
|
*
|
|
@@ -179,6 +200,18 @@ export type DogwoodLowerResult = {
|
|
|
179
200
|
export declare function parseValidateOutput(run: DogwoodRun): DogwoodValidateResult;
|
|
180
201
|
/** Read a `lower --format json` run. Same two-shape discipline as validate. */
|
|
181
202
|
export declare function parseLowerOutput(run: DogwoodRun): DogwoodLowerResult;
|
|
203
|
+
/**
|
|
204
|
+
* Read a `replay --format json` run.
|
|
205
|
+
*
|
|
206
|
+
* The one place the exit-code discipline matters most: **replay exits 0 even
|
|
207
|
+
* when every verdict is DENY.** A nonzero exit means the trace or the policy
|
|
208
|
+
* set failed to load, never that a policy denied — so a caller that read the
|
|
209
|
+
* exit code as a verdict would report a working deny-by-default policy set as
|
|
210
|
+
* a broken one. As everywhere else in this module, the JSON decides: a body
|
|
211
|
+
* carrying `verdicts` is the report, a body carrying `message` is the fatal
|
|
212
|
+
* `OpError` (a malformed trace line lands here), anything else is unusable.
|
|
213
|
+
*/
|
|
214
|
+
export declare function parseReplayOutput(run: DogwoodRun): DogwoodReplayResult;
|
|
182
215
|
/**
|
|
183
216
|
* The files one CLI invocation needs.
|
|
184
217
|
*
|
|
@@ -202,5 +235,13 @@ export interface DogwoodBundle {
|
|
|
202
235
|
export declare function runDogwoodValidate(binary: string, bundle: DogwoodBundle): DogwoodValidateResult;
|
|
203
236
|
/** Run `dogwood lower` over a bundle. Never throws. */
|
|
204
237
|
export declare function runDogwoodLower(binary: string, bundle: DogwoodBundle): DogwoodLowerResult;
|
|
238
|
+
/**
|
|
239
|
+
* Run `dogwood replay` over a bundle and a trace. Never throws.
|
|
240
|
+
*
|
|
241
|
+
* The trace is materialized beside the bundle's own files and passed as
|
|
242
|
+
* `--trace`; the same scratch directory is removed in the same `finally`, so a
|
|
243
|
+
* trace carrying a session's request payloads does not outlive the run.
|
|
244
|
+
*/
|
|
245
|
+
export declare function runDogwoodReplay(binary: string, bundle: DogwoodBundle, trace: string): DogwoodReplayResult;
|
|
205
246
|
export {};
|
|
206
247
|
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/dogwood/cli.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/dogwood/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/dogwood/index.d.ts
CHANGED
|
@@ -23,11 +23,17 @@ export { concrete, declaredEventKinds, defaultEventSchema, eventDeclaration, eve
|
|
|
23
23
|
export type { EventDeclaration, EventField, EventFieldType, EventSchema, EventSelector, NamedField, PinTarget, SpreadField, } from "./event-schema.js";
|
|
24
24
|
export { DOGWOOD_EVENT_SCHEMA_FILENAME, DOGWOOD_EVENT_SCHEMA_TYPE, DOGWOOD_LEXICON, DOGWOOD_MACRO_FILENAME, DOGWOOD_MACRO_LIBRARY_TYPE, DOGWOOD_POLICY_FILENAME, DOGWOOD_POLICY_TYPE, TemporalEventSchema, TemporalMacroLibrary, TemporalPolicy, } from "./policy.js";
|
|
25
25
|
export type { EventSchemaProps, MacroLibraryProps, TemporalPolicyProps } from "./policy.js";
|
|
26
|
-
export { DOGWOOD_EVENT_SCHEMA_SUFFIX, DOGWOOD_POLICY_SUFFIX, renderTemporalPolicyText, serializeDogwood, } from "./serialize.js";
|
|
27
|
-
export type { DogwoodSerializeResult } from "./serialize.js";
|
|
26
|
+
export { DOGWOOD_EVENT_SCHEMA_SUFFIX, DOGWOOD_POLICY_SUFFIX, dogwoodPolicyRecords, renderTemporalPolicyText, serializeDogwood, } from "./serialize.js";
|
|
27
|
+
export type { DogwoodPolicyRecord, DogwoodSerializeResult } from "./serialize.js";
|
|
28
28
|
export { blankComments, dogwoodPolicyFiles, dogwoodSchemaFiles, effectiveMaxWindowSeconds, readEventSchema, scanPredicates, scanWindowlessOperators, scanWindows, temporalRegions, } from "./scan.js";
|
|
29
29
|
export type { DogwoodArtifact, EventSchemaFacts, TemporalPredicateRef, WindowRef } from "./scan.js";
|
|
30
30
|
export { DOGWOOD_UPSTREAM } from "./upstream.js";
|
|
31
|
-
export { DOGWOOD_BINARY_ENV, DOGWOOD_BINARY_NAME, DOGWOOD_SEARCH_ORDER, configureDogwoodCli, findDogwoodBinary, formatDogwoodDiagnostic, parseLowerOutput, parseValidateOutput, resetDogwoodCli, runDogwoodLower, runDogwoodValidate, } from "./cli.js";
|
|
32
|
-
export type { DogwoodBinary, DogwoodBinarySource, DogwoodBundle, DogwoodDiagnostic, DogwoodFatal, DogwoodLabel, DogwoodLowerResult, DogwoodLowered, DogwoodRun, DogwoodRunner, DogwoodUnusable, DogwoodValidateResult, } from "./cli.js";
|
|
31
|
+
export { DOGWOOD_BINARY_ENV, DOGWOOD_BINARY_NAME, DOGWOOD_SEARCH_ORDER, configureDogwoodCli, findDogwoodBinary, formatDogwoodDiagnostic, parseLowerOutput, parseReplayOutput, parseValidateOutput, resetDogwoodCli, runDogwoodLower, runDogwoodReplay, runDogwoodValidate, } from "./cli.js";
|
|
32
|
+
export type { DogwoodBinary, DogwoodBinarySource, DogwoodBundle, DogwoodDiagnostic, DogwoodFatal, DogwoodLabel, DogwoodLowerResult, DogwoodLowered, DogwoodReplayResult, DogwoodRun, DogwoodRunner, DogwoodUnusable, DogwoodValidateResult, DogwoodVerdict, } from "./cli.js";
|
|
33
|
+
export { auditTrace, decimalValue, entityRef, rawValue, renderTrace, renderTraceFields, renderTraceLine, renderTraceValue, traceEntity, traceEvent, traceFixture, } from "./trace.js";
|
|
34
|
+
export type { TraceAuditOptions, TraceBags, TraceDecimal, TraceEntityDecl, TraceEntityRef, TraceEvent, TraceEventInput, TraceFields, TraceFixture, TraceFixtureOptions, TraceIssue, TraceIssueKind, TraceRaw, TraceScope, TraceTagged, TraceValue, } from "./trace.js";
|
|
35
|
+
export { compareVerdicts, dogwoodReplay, dogwoodReplayReport, renderReplaySummary, resolveReplayInputs, } from "./replay-activity.js";
|
|
36
|
+
export type { DogwoodReplayArgs, DogwoodReplayReportArgs, ExpectedVerdict, PolicyReplayDispatch, PolicyReplayMode, PolicyReplayReport, ReplayDivergence, ReplayExpectation, } from "./replay-activity.js";
|
|
37
|
+
export { DEFAULT_REPLAY_REPORT_PATH, PolicyReplayOp, dogwoodReplayReportStep, dogwoodReplayStep, } from "./replay-op.js";
|
|
38
|
+
export type { DogwoodReplayReportStepOpts, DogwoodReplayStepOpts, PolicyReplayOpConfig, PolicyReplayOpResources, } from "./replay-op.js";
|
|
33
39
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dogwood/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,MAAM,EACN,WAAW,EACX,aAAa,EACb,WAAW,GACZ,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE/F,OAAO,EACL,GAAG,EACH,OAAO,EACP,UAAU,EACV,IAAI,EACJ,IAAI,EACJ,OAAO,EACP,KAAK,EACL,GAAG,EACH,SAAS,EACT,SAAS,EACT,MAAM,EACN,QAAQ,EACR,GAAG,EACH,QAAQ,EACR,GAAG,EACH,SAAS,EACT,QAAQ,EACR,GAAG,EACH,eAAe,EACf,UAAU,EACV,QAAQ,EACR,cAAc,EACd,KAAK,EACL,GAAG,EACH,GAAG,EACH,cAAc,EACd,IAAI,EACJ,EAAE,EACF,WAAW,EACX,MAAM,EACN,QAAQ,GACT,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,OAAO,EACP,SAAS,EACT,QAAQ,EACR,cAAc,EACd,kBAAkB,EAClB,SAAS,EACT,UAAU,EACV,YAAY,EACZ,WAAW,EACX,QAAQ,EACR,OAAO,EACP,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,SAAS,EACT,OAAO,EACP,iBAAiB,EACjB,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,MAAM,EACN,WAAW,EACX,cAAc,GACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,mBAAmB,EACnB,IAAI,EACJ,mBAAmB,EACnB,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,SAAS,EACT,WAAW,EACX,qBAAqB,EACrB,kBAAkB,EAClB,SAAS,GACV,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE3D,OAAO,EACL,QAAQ,EACR,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,WAAW,EACX,KAAK,EACL,UAAU,EACV,YAAY,EACZ,WAAW,EACX,WAAW,EACX,aAAa,EACb,MAAM,EACN,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,aAAa,GACd,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,WAAW,EACX,aAAa,EACb,UAAU,EACV,SAAS,EACT,WAAW,GACZ,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,6BAA6B,EAC7B,yBAAyB,EACzB,eAAe,EACf,sBAAsB,EACtB,0BAA0B,EAC1B,uBAAuB,EACvB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,GACf,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAEzF,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,EACrB,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dogwood/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `dogwoodReplay` / `dogwoodReplayReport` — the replay half of PolicyReplayOp
|
|
3
|
+
* (#1661, epic #1646).
|
|
4
|
+
*
|
|
5
|
+
* Contributed the way the fly lexicon contributes `flyApply`: a plain exported
|
|
6
|
+
* async function taking one args object, re-exported from
|
|
7
|
+
* `src/op/activities/index.ts`, resolved **by name** by core's activity
|
|
8
|
+
* registry when a project lists the `cedar` lexicon. There is no Temporal
|
|
9
|
+
* import here and no Temporal dependency in the package — the local executor
|
|
10
|
+
* runs it as-is, and a Temporal worker registers the same function.
|
|
11
|
+
*
|
|
12
|
+
* What it does: takes a policy bundle (inline text or paths), an event trace
|
|
13
|
+
* (typed events, inline text, or a path) and a set of expectations, runs
|
|
14
|
+
* `dogwood replay --format json` through the existing CLI adapter in
|
|
15
|
+
* `./cli.ts`, and returns a typed divergence report — expected versus actual
|
|
16
|
+
* verdict per decision point, with the determining rules and per-evaluation
|
|
17
|
+
* errors carried through.
|
|
18
|
+
*
|
|
19
|
+
* Three contract details from the #1657 verification shape the code:
|
|
20
|
+
*
|
|
21
|
+
* - **Replay exits 0 even when every verdict is DENY.** A nonzero exit means
|
|
22
|
+
* the trace or the policy set failed to load. `./cli.ts` already refuses to
|
|
23
|
+
* read exit codes as verdicts; this module refuses to read a DENY as a
|
|
24
|
+
* failure.
|
|
25
|
+
* - **A run that could not happen is not a run that found nothing.** An
|
|
26
|
+
* unusable invocation or a fatal (a malformed trace line, an unparseable
|
|
27
|
+
* policy set) throws, so the step fails instead of reporting zero
|
|
28
|
+
* divergences.
|
|
29
|
+
* - **`index` is the decision-stream position, not the trace line number.** A
|
|
30
|
+
* history-only event contributes history and no verdict, so expectations
|
|
31
|
+
* written against trace lines would silently address the wrong decision.
|
|
32
|
+
* Expectations therefore match on `timestamp` by default, and on `index`
|
|
33
|
+
* only when the caller says so.
|
|
34
|
+
*
|
|
35
|
+
* The trace input is deliberately generic. An AgentCore session/decision
|
|
36
|
+
* history is the follow-on source (it needs the aws lexicon's activity
|
|
37
|
+
* surface, out of scope here) — a trace is a trace, wherever it came from.
|
|
38
|
+
*/
|
|
39
|
+
import { type DogwoodBundle, type DogwoodVerdict } from "./cli.js";
|
|
40
|
+
import { type TraceEvent, type TraceIssue } from "./trace.js";
|
|
41
|
+
/** What a replay produces on divergence, mirroring `WorkflowAuditOp`'s modes. */
|
|
42
|
+
export type PolicyReplayMode = "report" | "issue" | "pull-request";
|
|
43
|
+
/** A verdict a decision point is expected to reach. */
|
|
44
|
+
export type ExpectedVerdict = "allow" | "deny";
|
|
45
|
+
/**
|
|
46
|
+
* One expectation against the decision stream.
|
|
47
|
+
*
|
|
48
|
+
* Give a `timestamp` (the `@<n>` of the line) or an `index` (the 0-based
|
|
49
|
+
* position in the decision stream). Prefer `timestamp`: it survives a trace
|
|
50
|
+
* gaining a history-only event, which shifts every later index.
|
|
51
|
+
*/
|
|
52
|
+
export interface ReplayExpectation {
|
|
53
|
+
readonly timestamp?: number;
|
|
54
|
+
readonly index?: number;
|
|
55
|
+
readonly verdict: ExpectedVerdict;
|
|
56
|
+
/** When set, the `.dw` rule indices the decision must be determined by. */
|
|
57
|
+
readonly determiningRules?: readonly number[];
|
|
58
|
+
/** What this decision point is proving, carried into the report. */
|
|
59
|
+
readonly note?: string;
|
|
60
|
+
}
|
|
61
|
+
/** One expected-versus-actual mismatch. */
|
|
62
|
+
export interface ReplayDivergence {
|
|
63
|
+
/** Decision-stream index. `-1` when the expected decision point never occurred. */
|
|
64
|
+
readonly index: number;
|
|
65
|
+
readonly timestamp: number;
|
|
66
|
+
/** Absent when the replay produced a decision nothing expected. */
|
|
67
|
+
readonly expected?: ExpectedVerdict;
|
|
68
|
+
/** Absent when an expected decision point produced no verdict at all. */
|
|
69
|
+
readonly actual?: ExpectedVerdict;
|
|
70
|
+
readonly determiningRules: readonly number[];
|
|
71
|
+
readonly errors: readonly string[];
|
|
72
|
+
readonly detail: string;
|
|
73
|
+
readonly note?: string;
|
|
74
|
+
}
|
|
75
|
+
/** What `dogwoodReplay` returns and `dogwoodReplayReport` reads back. */
|
|
76
|
+
export interface PolicyReplayReport {
|
|
77
|
+
/** True when nothing diverged and no decision point errored. */
|
|
78
|
+
readonly ok: boolean;
|
|
79
|
+
readonly mode: PolicyReplayMode;
|
|
80
|
+
/** Every decision point, in stream order. */
|
|
81
|
+
readonly verdicts: readonly DogwoodVerdict[];
|
|
82
|
+
readonly divergences: readonly ReplayDivergence[];
|
|
83
|
+
/** Divergence count — the number a search attribute or a gate reads. */
|
|
84
|
+
readonly findings: number;
|
|
85
|
+
/** Trace weaknesses found by `auditTrace`, when typed events were supplied. */
|
|
86
|
+
readonly traceIssues: readonly TraceIssue[];
|
|
87
|
+
/** Markdown, used as the report body or an issue/PR body. */
|
|
88
|
+
readonly summary: string;
|
|
89
|
+
}
|
|
90
|
+
/** What `dogwoodReplay` takes. Every artifact is inline text or a path. */
|
|
91
|
+
export interface DogwoodReplayArgs {
|
|
92
|
+
/** `.dw` policy set text. */
|
|
93
|
+
policies?: string;
|
|
94
|
+
/** …or a path to it. Relative paths resolve against {@link cwd}. */
|
|
95
|
+
policiesPath?: string;
|
|
96
|
+
/** Cedar action schema text (`--policy-schema`). Not optional to the CLI. */
|
|
97
|
+
policySchema?: string;
|
|
98
|
+
policySchemaPath?: string;
|
|
99
|
+
/** `.dwschema` event schema text (`--event-schema`). */
|
|
100
|
+
eventSchema?: string;
|
|
101
|
+
eventSchemaPath?: string;
|
|
102
|
+
/** `.dw` macro library text (`--macros`). */
|
|
103
|
+
macros?: string;
|
|
104
|
+
macrosPath?: string;
|
|
105
|
+
/** `providers.json` text (`--providers`). Rhai must be inlined under `implementation.script`. */
|
|
106
|
+
providers?: string;
|
|
107
|
+
providersPath?: string;
|
|
108
|
+
/** Typed events — rendered here, and audited for the both-bags trap. */
|
|
109
|
+
traceEvents?: readonly TraceEvent[];
|
|
110
|
+
/** …or the trace text as it would appear in a `.log`. */
|
|
111
|
+
trace?: string;
|
|
112
|
+
/** …or a path to it. */
|
|
113
|
+
tracePath?: string;
|
|
114
|
+
/**
|
|
115
|
+
* Event kinds that decide, for the trace audit. Default `["request"]` — the
|
|
116
|
+
* truth is whichever kinds the `.dwschema` marks `decision`.
|
|
117
|
+
*/
|
|
118
|
+
traceDecisionKinds?: readonly string[];
|
|
119
|
+
/** What each decision point must decide. An empty list replays and reports. */
|
|
120
|
+
expect?: readonly ReplayExpectation[];
|
|
121
|
+
/** Default `report`. */
|
|
122
|
+
mode?: PolicyReplayMode;
|
|
123
|
+
/** Explicit binary path. Otherwise resolved by {@link findDogwoodBinary}. */
|
|
124
|
+
binary?: string;
|
|
125
|
+
/** Base directory for every relative path. Default `process.cwd()`. */
|
|
126
|
+
cwd?: string;
|
|
127
|
+
/** When set, the report is written here as JSON for the Report phase to read. */
|
|
128
|
+
reportPath?: string;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Assemble the bundle and the trace from whichever form the caller supplied.
|
|
132
|
+
*
|
|
133
|
+
* Exported because the composite's Artifacts phase writes files and the Replay
|
|
134
|
+
* phase names them: resolving the same way in a test as in the Op is the point
|
|
135
|
+
* of having one function do it.
|
|
136
|
+
*/
|
|
137
|
+
export declare function resolveReplayInputs(args: DogwoodReplayArgs): Promise<{
|
|
138
|
+
bundle: DogwoodBundle;
|
|
139
|
+
trace: string;
|
|
140
|
+
traceIssues: TraceIssue[];
|
|
141
|
+
}>;
|
|
142
|
+
/**
|
|
143
|
+
* Match expectations against the decision stream.
|
|
144
|
+
*
|
|
145
|
+
* Timestamp matching consumes verdicts left to right, so two expectations at
|
|
146
|
+
* the same `@n` address the first and second decision there rather than both
|
|
147
|
+
* addressing the first. A verdict nothing expected is reported too — a policy
|
|
148
|
+
* set that starts deciding somewhere new is drift, and the usual reason a
|
|
149
|
+
* replay is being run at all.
|
|
150
|
+
*/
|
|
151
|
+
export declare function compareVerdicts(verdicts: readonly DogwoodVerdict[], expectations: readonly ReplayExpectation[]): ReplayDivergence[];
|
|
152
|
+
/** The markdown body — printed in `report` mode, posted in the other two. */
|
|
153
|
+
export declare function renderReplaySummary(report: Omit<PolicyReplayReport, "summary">): string;
|
|
154
|
+
/**
|
|
155
|
+
* Replay a policy bundle against a trace and report divergence.
|
|
156
|
+
*
|
|
157
|
+
* Throws when the CLI could not be used or the run was fatal. That is the
|
|
158
|
+
* distinction `./cli.ts` draws and this preserves: a replay that did not
|
|
159
|
+
* happen must fail the step, never report zero divergences.
|
|
160
|
+
*/
|
|
161
|
+
export declare function dogwoodReplay(args: DogwoodReplayArgs): Promise<PolicyReplayReport>;
|
|
162
|
+
/** What `dogwoodReplayReport` takes. */
|
|
163
|
+
export interface DogwoodReplayReportArgs {
|
|
164
|
+
/** The JSON `dogwoodReplay` wrote. Relative paths resolve against {@link cwd}. */
|
|
165
|
+
reportPath?: string;
|
|
166
|
+
/** …or the report itself, for a caller holding it already. */
|
|
167
|
+
report?: PolicyReplayReport;
|
|
168
|
+
/** Overrides the mode recorded in the report. */
|
|
169
|
+
mode?: PolicyReplayMode;
|
|
170
|
+
/** Title for the issue or pull request. */
|
|
171
|
+
title?: string;
|
|
172
|
+
cwd?: string;
|
|
173
|
+
/** Fail the step when the replay diverged. Default false — the mode decides. */
|
|
174
|
+
failOnDivergence?: boolean;
|
|
175
|
+
}
|
|
176
|
+
/** What the Report phase produces. */
|
|
177
|
+
export interface PolicyReplayDispatch {
|
|
178
|
+
readonly mode: PolicyReplayMode;
|
|
179
|
+
readonly findings: number;
|
|
180
|
+
readonly title: string;
|
|
181
|
+
/** The markdown to print, or to use as an issue/PR body. */
|
|
182
|
+
readonly body: string;
|
|
183
|
+
/** True when there is something to say — an issue/PR is only worth opening then. */
|
|
184
|
+
readonly actionable: boolean;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Turn a written replay report into the thing the finding mode calls for.
|
|
188
|
+
*
|
|
189
|
+
* It renders and returns; it does not open anything. Same as
|
|
190
|
+
* `workflowSupplyChainAudit`, and for the same reason — the cedar lexicon has
|
|
191
|
+
* no forge client and should not grow one to reach GitHub, GitLab or Forgejo.
|
|
192
|
+
* `report` mode prints the body; `issue` and `pull-request` hand back the
|
|
193
|
+
* title and body for whatever step opens them.
|
|
194
|
+
*/
|
|
195
|
+
export declare function dogwoodReplayReport(args: DogwoodReplayReportArgs): Promise<PolicyReplayDispatch>;
|
|
196
|
+
//# sourceMappingURL=replay-activity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay-activity.d.ts","sourceRoot":"","sources":["../../src/dogwood/replay-activity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `PolicyReplayOp` — replay a declared `.dw` set against a recorded event
|
|
3
|
+
* trace and report divergence (#1661, epic #1646).
|
|
4
|
+
*
|
|
5
|
+
* Shape follows `WorkflowAuditOp`: an observe-dial Op whose last phase is a
|
|
6
|
+
* finding mode (`report | issue | pull-request`). What it observes is not a
|
|
7
|
+
* moving upstream but a moving *history* — a policy set that was correct
|
|
8
|
+
* against last week's traffic can decide differently against this week's, and
|
|
9
|
+
* the deterministic build cannot see that.
|
|
10
|
+
*
|
|
11
|
+
* ## Packaging: the composite ships from cedar, not from temporal
|
|
12
|
+
*
|
|
13
|
+
* `WorkflowAuditOp` and `PipelineAuditOp` live in the temporal lexicon because
|
|
14
|
+
* they hand back a `TemporalSchedule` alongside the Op, and that resource is
|
|
15
|
+
* temporal's. This composite hands back an Op and nothing else, so it has no
|
|
16
|
+
* reason to reach across — it imports `@intentius/chant/op` only, exactly as
|
|
17
|
+
* the fly lexicon's `flyDeploy` composite does. cedar therefore keeps zero
|
|
18
|
+
* runtime dependency on `@intentius/chant-lexicon-temporal`.
|
|
19
|
+
*
|
|
20
|
+
* The scheduled form is a two-line project-side pairing rather than a config
|
|
21
|
+
* flag, and that is the deliberate cost of the decision:
|
|
22
|
+
*
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { TemporalSchedule } from "@intentius/chant-lexicon-temporal";
|
|
25
|
+
*
|
|
26
|
+
* export const { op } = PolicyReplayOp({ name: "policy-replay", … });
|
|
27
|
+
* export const schedule = new TemporalSchedule({
|
|
28
|
+
* scheduleId: "policy-replay-schedule",
|
|
29
|
+
* spec: { cronExpressions: ["0 6 * * *"] },
|
|
30
|
+
* action: { workflowType: "policyReplayWorkflow", taskQueue: "policy-replay" },
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* A project that wants that already installs the temporal lexicon; a project
|
|
35
|
+
* that only wants `chant run policy-replay` on the local executor should not
|
|
36
|
+
* have to. `examples/policy-replay` is the worked recipe for both.
|
|
37
|
+
*
|
|
38
|
+
* ## Phases
|
|
39
|
+
*
|
|
40
|
+
* ```
|
|
41
|
+
* Artifacts chantBuild — emit policies.dw, the .cedarschema and the
|
|
42
|
+
* .dwschema the replay reads (skippable when they are checked in)
|
|
43
|
+
* Replay dogwoodReplay — run `dogwood replay --format json` over the
|
|
44
|
+
* bundle and the trace, write the divergence report
|
|
45
|
+
* Report dogwoodReplayReport — read that report and act on the mode
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* The report file is the seam between the last two phases, the same way
|
|
49
|
+
* `dist/fly.json` is the seam between `build:fly` and `flyApply`: Op steps do
|
|
50
|
+
* not pass return values to one another, so a phase boundary needs an artifact
|
|
51
|
+
* to be a real boundary rather than a cosmetic one.
|
|
52
|
+
*/
|
|
53
|
+
import { type ActivityStep, type OpResource } from "@intentius/chant/op";
|
|
54
|
+
import type { PolicyReplayMode, ReplayExpectation } from "./replay-activity.js";
|
|
55
|
+
/** Where the Replay phase writes its report by default. */
|
|
56
|
+
export declare const DEFAULT_REPLAY_REPORT_PATH = "dist/dogwood-replay.json";
|
|
57
|
+
/** Options for {@link dogwoodReplayStep}. Mirrors `DogwoodReplayArgs`. */
|
|
58
|
+
export interface DogwoodReplayStepOpts {
|
|
59
|
+
policies?: string;
|
|
60
|
+
policiesPath?: string;
|
|
61
|
+
policySchema?: string;
|
|
62
|
+
policySchemaPath?: string;
|
|
63
|
+
eventSchema?: string;
|
|
64
|
+
eventSchemaPath?: string;
|
|
65
|
+
macros?: string;
|
|
66
|
+
macrosPath?: string;
|
|
67
|
+
providers?: string;
|
|
68
|
+
providersPath?: string;
|
|
69
|
+
trace?: string;
|
|
70
|
+
tracePath?: string;
|
|
71
|
+
expect?: readonly ReplayExpectation[];
|
|
72
|
+
mode?: PolicyReplayMode;
|
|
73
|
+
binary?: string;
|
|
74
|
+
cwd?: string;
|
|
75
|
+
reportPath?: string;
|
|
76
|
+
profile?: ActivityStep["profile"];
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Replay a policy bundle against a trace — the typed twin of `flyApplyStep`.
|
|
80
|
+
*
|
|
81
|
+
* Resolves to the `dogwoodReplay` activity by name, which `loadActivities`
|
|
82
|
+
* binds when the project lists the `cedar` lexicon. Defaults to the
|
|
83
|
+
* `policyCheck` profile.
|
|
84
|
+
*/
|
|
85
|
+
export declare const dogwoodReplayStep: (opts: DogwoodReplayStepOpts) => ActivityStep;
|
|
86
|
+
/** Options for {@link dogwoodReplayReportStep}. */
|
|
87
|
+
export interface DogwoodReplayReportStepOpts {
|
|
88
|
+
reportPath?: string;
|
|
89
|
+
mode?: PolicyReplayMode;
|
|
90
|
+
title?: string;
|
|
91
|
+
cwd?: string;
|
|
92
|
+
failOnDivergence?: boolean;
|
|
93
|
+
profile?: ActivityStep["profile"];
|
|
94
|
+
}
|
|
95
|
+
/** Read a written replay report and act on its finding mode. */
|
|
96
|
+
export declare const dogwoodReplayReportStep: (opts?: DogwoodReplayReportStepOpts) => ActivityStep;
|
|
97
|
+
/** Options for {@link PolicyReplayOp}. */
|
|
98
|
+
export interface PolicyReplayOpConfig {
|
|
99
|
+
/** Op name (kebab-case) — the `chant run <name>` target and the task queue base. */
|
|
100
|
+
name: string;
|
|
101
|
+
overview?: string;
|
|
102
|
+
/** Defaults to {@link name}. */
|
|
103
|
+
taskQueue?: string;
|
|
104
|
+
/**
|
|
105
|
+
* Directory every relative path below resolves against, and the one the
|
|
106
|
+
* build script runs in. Default `.`.
|
|
107
|
+
*/
|
|
108
|
+
path?: string;
|
|
109
|
+
/**
|
|
110
|
+
* npm script that emits the `.dw` bundle. Default `build`. Pass `false` when
|
|
111
|
+
* the artifacts are checked in — the Artifacts phase is then omitted rather
|
|
112
|
+
* than run as a no-op, so the Op's phase list says what it really does.
|
|
113
|
+
*/
|
|
114
|
+
buildScript?: string | false;
|
|
115
|
+
/** Emitted `.dw` policy set. Default `dist/policies.dw`. */
|
|
116
|
+
policiesPath?: string;
|
|
117
|
+
/** Emitted Cedar action schema. Required by the CLI; default `dist/app.cedarschema`. */
|
|
118
|
+
policySchemaPath?: string;
|
|
119
|
+
/** Emitted `.dwschema`, when the project declares one. */
|
|
120
|
+
eventSchemaPath?: string;
|
|
121
|
+
/** Emitted macro library, when the project ships one out of line. */
|
|
122
|
+
macrosPath?: string;
|
|
123
|
+
/** `providers.json`, with the Rhai inlined under `implementation.script`. */
|
|
124
|
+
providersPath?: string;
|
|
125
|
+
/** The recorded trace to replay. Required — there is nothing to replay without one. */
|
|
126
|
+
tracePath: string;
|
|
127
|
+
/** What each decision point must decide. Empty replays and reports verdicts. */
|
|
128
|
+
expect?: readonly ReplayExpectation[];
|
|
129
|
+
/** What to produce on divergence. Default `report`. */
|
|
130
|
+
onFinding?: PolicyReplayMode;
|
|
131
|
+
/** Title for the issue or pull request the finding mode calls for. */
|
|
132
|
+
title?: string;
|
|
133
|
+
/**
|
|
134
|
+
* Fail the Op when the replay diverged. Default false — an observe-dial Op
|
|
135
|
+
* reports by default, and a red run is a decision the caller makes.
|
|
136
|
+
*/
|
|
137
|
+
failOnDivergence?: boolean;
|
|
138
|
+
/** Where the Replay phase writes its report. Default {@link DEFAULT_REPLAY_REPORT_PATH}. */
|
|
139
|
+
reportPath?: string;
|
|
140
|
+
/** Explicit `dogwood` binary path, for a runner that knows where it built one. */
|
|
141
|
+
binary?: string;
|
|
142
|
+
}
|
|
143
|
+
/** What {@link PolicyReplayOp} hands back. */
|
|
144
|
+
export interface PolicyReplayOpResources {
|
|
145
|
+
/** The Op — discovered by `chant run <name>`, emitted by `chant build`. */
|
|
146
|
+
op: InstanceType<typeof OpResource>;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Assemble the replay Op: emit the artifacts, replay them, act on the finding.
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```typescript
|
|
153
|
+
* export const { op } = PolicyReplayOp({
|
|
154
|
+
* name: "policy-replay",
|
|
155
|
+
* tracePath: "trace/read-after-login.log",
|
|
156
|
+
* expect: [
|
|
157
|
+
* { timestamp: 10, verdict: "allow", determiningRules: [0] },
|
|
158
|
+
* { timestamp: 7200, verdict: "deny", note: "the login is two hours stale" },
|
|
159
|
+
* ],
|
|
160
|
+
* onFinding: "issue",
|
|
161
|
+
* });
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
export declare function PolicyReplayOp(config: PolicyReplayOpConfig): PolicyReplayOpResources;
|
|
165
|
+
//# sourceMappingURL=replay-op.d.ts.map
|