anymorph 0.2.6 → 0.4.0

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.
Files changed (63) hide show
  1. package/README.md +50 -36
  2. package/dist/index.js +9184 -251
  3. package/dist/skillpacks/geo/scaffold/AGENTS.md +38 -0
  4. package/dist/skillpacks/geo/scaffold/CLAUDE.md +33 -0
  5. package/dist/skillpacks/geo/shared/evidence-principles.md +35 -0
  6. package/dist/skillpacks/geo/shared/geo-principles.md +281 -0
  7. package/dist/skillpacks/geo/shared/vertical-playbooks/beauty.md +65 -0
  8. package/dist/skillpacks/geo/shared/vertical-playbooks/commerce.md +65 -0
  9. package/dist/skillpacks/geo/shared/vertical-playbooks/ota.md +62 -0
  10. package/dist/skillpacks/geo/shared/vertical-playbooks/saas.md +64 -0
  11. package/dist/skillpacks/geo/skills/brand-owned-diagnosis/SKILL.md +1 -1
  12. package/dist/skillpacks/geo/skills/brand-owned-diagnosis/references/diagnosis-contract.md +8 -9
  13. package/dist/skillpacks/geo/skills/brand-owned-diagnosis/references/workflow.md +11 -8
  14. package/dist/skillpacks/geo/skills/geo-generating-actions/SKILL.md +110 -22
  15. package/dist/skillpacks/geo/skills/geo-generating-actions/references/orchestrator.workflow.md +171 -5
  16. package/dist/skillpacks/geo/skills/geo-generating-actions/scripts/geo-scaffold.mjs +358 -0
  17. package/dist/skillpacks/geo/skills/geo-generating-actions/scripts/geo.mjs +66 -0
  18. package/dist/skillpacks/geo/skills/geo-initializing-strategy/references/foundation-diagnosis.md +1 -1
  19. package/dist/skillpacks/geo/skills/geo-local-setup/SKILL.md +66 -0
  20. package/dist/skillpacks/geo/skills/geo-local-setup/agents/openai.yaml +4 -0
  21. package/dist/skillpacks/geo/skills/geo-page-writer/SKILL.md +81 -0
  22. package/dist/skillpacks/geo/skills/geo-page-writer/agents/openai.yaml +4 -0
  23. package/dist/skillpacks/geo/skills/geo-page-writer/references/research.md +61 -0
  24. package/dist/skillpacks/geo/skills/geo-page-writer/references/validation.md +59 -0
  25. package/dist/skillpacks/geo/skills/geo-page-writer/references/writing.md +55 -0
  26. package/dist/skillpacks/geo/skills/geo-page-writer/scripts/check-page-mdx.mjs +210 -0
  27. package/dist/skillpacks/geo/skills/geo-page-writer/scripts/collect-page-sources.mjs +303 -0
  28. package/dist/skillpacks/geo/skills/geo-pages-diagnosis/SKILL.md +1 -1
  29. package/dist/skillpacks/geo/skills/geo-pages-diagnosis/references/diagnosis-contract.md +36 -17
  30. package/dist/skillpacks/geo/skills/geo-pages-diagnosis/references/workflow.md +65 -15
  31. package/dist/skillpacks/geo/skills/seo-ecommerce-opportunity/SKILL.md +82 -0
  32. package/dist/skillpacks/geo/skills/seo-ecommerce-opportunity/agents/openai.yaml +5 -0
  33. package/dist/skillpacks/geo/skills/seo-ecommerce-opportunity/references/ecommerce-rules.md +31 -0
  34. package/dist/skillpacks/geo/skills/seo-internal-link-opportunity/SKILL.md +57 -0
  35. package/dist/skillpacks/geo/skills/seo-internal-link-opportunity/agents/openai.yaml +5 -0
  36. package/dist/skillpacks/geo/skills/seo-internal-link-opportunity/references/link-rules.md +28 -0
  37. package/dist/skillpacks/geo/skills/seo-opportunity-audit/SKILL.md +141 -0
  38. package/dist/skillpacks/geo/skills/seo-opportunity-audit/agents/openai.yaml +5 -0
  39. package/dist/skillpacks/geo/skills/seo-opportunity-audit/references/action-contract.md +62 -0
  40. package/dist/skillpacks/geo/skills/seo-opportunity-audit/scripts/seo-toolkit.mjs +248 -0
  41. package/dist/skillpacks/geo/skills/seo-page-diagnosis/SKILL.md +56 -0
  42. package/dist/skillpacks/geo/skills/seo-page-diagnosis/agents/openai.yaml +5 -0
  43. package/dist/skillpacks/geo/skills/seo-page-diagnosis/references/page-checks.md +38 -0
  44. package/dist/skillpacks/geo/skills/seo-serp-opportunity-research/SKILL.md +66 -0
  45. package/dist/skillpacks/geo/skills/seo-serp-opportunity-research/agents/openai.yaml +5 -0
  46. package/dist/skillpacks/geo/skills/seo-serp-opportunity-research/references/page-type-taxonomy.md +40 -0
  47. package/dist/skillpacks/geo/skills/seo-serp-opportunity-research/references/serp-methodology.md +38 -0
  48. package/dist/skillpacks/geo/skills/seo-technical-diagnosis/SKILL.md +64 -0
  49. package/dist/skillpacks/geo/skills/seo-technical-diagnosis/agents/openai.yaml +5 -0
  50. package/dist/skillpacks/geo/skills/seo-technical-diagnosis/references/checks.md +58 -0
  51. package/dist/skillpacks/geo/skills/third-party-diagnosis/SKILL.md +1 -1
  52. package/dist/skillpacks/geo/skills/third-party-diagnosis/references/diagnosis-contract.md +2 -2
  53. package/dist/skillpacks/geo/skills/third-party-diagnosis/references/workflow.md +1 -1
  54. package/dist/skillpacks/geo/skills/third-party-execution-planning/SKILL.md +64 -0
  55. package/dist/skillpacks/geo/skills/third-party-execution-planning/agents/openai.yaml +4 -0
  56. package/dist/skillpacks/geo/skills/third-party-execution-planning/references/execution-contract.md +90 -0
  57. package/dist/skillpacks/geo/skills/third-party-execution-planning/references/non-social-surface-playbooks.md +123 -0
  58. package/package.json +2 -1
  59. package/dist/skillpacks/geo/skills/social-execution-planning/SKILL.md +0 -53
  60. package/dist/skillpacks/geo/skills/social-execution-planning/agents/openai.yaml +0 -5
  61. package/dist/skillpacks/geo/skills/social-execution-planning/references/execution-contract.md +0 -68
  62. /package/dist/skillpacks/geo/skills/{social-execution-planning → third-party-execution-planning}/references/reddit-rules.md +0 -0
  63. /package/dist/skillpacks/geo/skills/{social-execution-planning/references/platform-playbooks.md → third-party-execution-planning/references/social-platform-playbooks.md} +0 -0
@@ -32,7 +32,7 @@ Read these references before producing proposals:
32
32
  - `references/diagnosis-contract.md` for the exact runtime diagnosis contract and artifact submission shape.
33
33
 
34
34
  1. Read shared run context and the brand-owned candidate slice.
35
- 2. Use `list_geo_strategy_candidates` before broad SQL.
35
+ 2. Use `list_brand_owned_candidates` before broad SQL.
36
36
  3. Use `get_geo_intent_diagnostic` for intent-level explanation.
37
37
  4. Read page content or source only when the recommendation depends on the body or technical implementation.
38
38
  5. Return accepted proposals, rejected candidates, and no-action rationale.
@@ -19,10 +19,10 @@ Do not propose GEO page generation unless the orchestrator explicitly asks you t
19
19
  Use the cheapest sufficient evidence.
20
20
 
21
21
  1. Common run context and candidate rows from the orchestrator.
22
- 2. `list_geo_strategy_candidates` for the first brand-owned candidate slice.
22
+ 2. `list_brand_owned_candidates` for the first brand-owned candidate slice.
23
23
  3. `get_geo_intent_diagnostic` for one intent before deeper checks.
24
- 4. `get_page_insights`, `get_page_visibility`, or `list_ai_responses` for page/prompt evidence.
25
- 5. `get_page_content` or `get_page_source_code` only when content or technical diagnosis depends on the page body.
24
+ 4. `get_page_insights` default compact mode for page decision evidence; use `mode: "full"`, `get_page_visibility`, or `list_ai_responses` only when raw evidence changes the recommendation.
25
+ 5. `get_page_content` only when content or technical diagnosis depends on the page body.
26
26
  6. `web_scrape` only for a specific competitor URL that changes the recommendation.
27
27
 
28
28
  Do not scrape broadly.
@@ -49,14 +49,13 @@ Use `authority_gap` only with external citation evidence or repeated competitor
49
49
 
50
50
  Allowed families:
51
51
 
52
- - `refresh_existing_page`
52
+ - `update_existing_page`
53
53
  - `retarget_existing_page`
54
- - `improve_entity_clarity`
55
- - `add_proof`
56
- - `add_comparison_coverage`
57
- - `fix_technical_issue`
54
+ - `merge_or_redirect_page`
58
55
  - `no_action`
59
56
 
57
+ Keep specific fixes such as proof, entity clarity, comparison coverage, and technical repairs in `rootCauses` and `operations`, not as separate action families.
58
+
60
59
  ## Output
61
60
 
62
61
  Produce one `brand_owned` diagnosis object in this shape.
@@ -78,7 +77,7 @@ Use this shape:
78
77
  "rootCauses": [
79
78
  { "bucket": "proof_gap", "confidence": 0.72, "evidence": ["Specific evidence summary"] }
80
79
  ],
81
- "actionFamily": "add_proof",
80
+ "actionFamily": "update_existing_page",
82
81
  "operations": ["Add customer proof near the answer block"],
83
82
  "priority": "high",
84
83
  "confidence": "medium",
@@ -142,15 +142,18 @@ Use `authority_gap` only when supported by external citation evidence or repeate
142
142
 
143
143
  Allowed action families:
144
144
 
145
- - `refresh_existing_page`
145
+ - `update_existing_page`
146
146
  - `retarget_existing_page`
147
- - `improve_entity_clarity`
148
- - `add_proof`
149
- - `add_comparison_coverage`
150
- - `fix_technical_issue`
147
+ - `merge_or_redirect_page`
151
148
  - `no_action`
152
149
 
153
- Prefer update actions over create actions when an existing discovered page clearly owns the intent.
150
+ Use `update_existing_page` when the owned page is the right surface and needs content, proof, entity clarity, comparison coverage, internal linking, or technical fixes.
151
+
152
+ Use `retarget_existing_page` when the page is a useful owned surface but should target a different intent or query cluster than the one being evaluated.
153
+
154
+ Use `merge_or_redirect_page` when the page should not remain an independent surface because it duplicates, cannibalizes, or conflicts with a stronger owned page.
155
+
156
+ Prefer owned-page updates over generated GEO page creation when an existing discovered page clearly owns the intent.
154
157
 
155
158
  ### 6. Output
156
159
 
@@ -172,7 +175,7 @@ Return a compact diagnosis for each accepted action:
172
175
  If a discovered page exists, visibility is low, and the page only gives generic education while competitor pages provide comparison criteria and proof:
173
176
 
174
177
  - root cause: `comparison_gap`, `proof_gap`
175
- - action family: `add_comparison_coverage`
178
+ - action family: `update_existing_page`
176
179
  - operation: add comparison criteria and customer proof to the existing page
177
180
 
178
181
  ### Cited But Not Mentioned
@@ -180,7 +183,7 @@ If a discovered page exists, visibility is low, and the page only gives generic
180
183
  If the page is cited but the AI answer does not name the tenant brand:
181
184
 
182
185
  - root cause: `entity_clarity_gap`
183
- - action family: `improve_entity_clarity`
186
+ - action family: `update_existing_page`
184
187
  - operation: strengthen brand naming, publisher schema, and product/company relationship in the answer block
185
188
 
186
189
  ## Guardrails
@@ -16,65 +16,97 @@ Use these skills for channel-specific diagnosis:
16
16
  - `$brand-owned-diagnosis` for customer-owned pages.
17
17
  - `$geo-pages-diagnosis` for generated GEO pages.
18
18
  - `$third-party-diagnosis` for Earn and external authority work.
19
+ - `$third-party-execution-planning` for selected third-party Earn actions that need human-executable social, directory, review, listing, marketplace, partner, or editorial plans.
20
+ - `$seo-opportunity-audit` for a full SEO opportunity pass when technical, page, SERP, internal-link, or ecommerce SEO evidence is needed.
21
+ - Focused SEO skills for narrow SEO evidence: `$seo-technical-diagnosis`, `$seo-page-diagnosis`, `$seo-serp-opportunity-research`, `$seo-internal-link-opportunity`, and `$seo-ecommerce-opportunity`.
19
22
 
20
23
  ## Required Reference
21
24
 
22
25
  Read `references/orchestrator.workflow.md` before final action selection. It contains the full production workflow: context construction, channel invocation order, proposal merge rules, conflict resolution, no-action discipline, and final artifact contract.
23
26
 
27
+ Also read shared references when relevant:
28
+
29
+ - `../../shared/evidence-principles.md` before accepting claim-sensitive,
30
+ freshness-sensitive, schema/feed, product/SKU, comparison, review, or
31
+ compliance-sensitive proposals.
32
+ - `../../shared/vertical-playbooks/{vertical}.md` when the tenant vertical is
33
+ known. Load only matching playbooks, such as `beauty.md`, `saas.md`,
34
+ `ota.md`, or `commerce.md`; use them as vertical heuristics, not as proof.
35
+ Tenant-specific strategy in `agent/STRATEGY.md` overrides generic playbooks.
36
+
24
37
  ## Workflow
25
38
 
26
39
  1. Read host-created run files and stable tenant memory.
27
- 2. Build one compact shared run context.
28
- 3. Invoke or apply the three channel diagnosis skills.
29
- 4. Read channel outputs and reject weak, duplicate, or conflicting proposals.
30
- 5. Prefer no action over low-evidence action.
31
- 6. In local CLI mode, write `agent/runs/{runId}/actions.json` directly.
32
- 7. Write rationale and status files only under the current run path.
33
- 8. Update durable tenant memory only when the run changes stable strategy or repeated learnings.
34
-
35
- ## Local CLI Mode
40
+ 2. Load relevant shared evidence and vertical references only when they affect
41
+ judgment.
42
+ 3. Build one compact shared run context.
43
+ 4. Invoke or apply the three channel diagnosis skills.
44
+ 5. For selected third-party proposals, invoke or apply `$third-party-execution-planning` before writing final actions.
45
+ 6. Persist each channel diagnosis under `agent/runs/{runId}/subagents/` as JSON plus compact `*.rationale.md`.
46
+ 7. When SEO evidence is needed, persist SEO skill artifacts under `agent/seo/{skillName}/{runId}/` and reference them from final proposals.
47
+ 8. Read channel outputs and reject weak, duplicate, or conflicting proposals.
48
+ 9. Prefer no action over low-evidence action.
49
+ 10. In hosted remote mode, submit the final artifact with the provided runtime artifact tool. In local mode only, write `agent/runs/{runId}/actions.json` directly.
50
+ 11. Write rationale and status files only under the current run path.
51
+ 12. Update durable tenant memory only when the run changes stable strategy or repeated learnings.
52
+
53
+ ## Local Skillpack Mode
36
54
 
37
55
  When running inside a tenant repo, this skill expects a prepared run directory.
38
56
  Do not invent a `runId` or create fake input files.
39
57
 
40
- Use the CLI to prepare the same filesystem contract that remote runners use.
58
+ Use the top-level `anymorph` CLI to prepare the same filesystem contract that
59
+ remote runners use. For first-time setup or repair, use `$geo-local-setup`.
41
60
 
42
61
  ```bash
43
- anymorph --version
44
62
  anymorph status
45
- anymorph geo init --repo .
46
- anymorph geo doctor --repo .
47
- anymorph geo prepare <workspace-domain-or-id> --repo .
63
+ anymorph login
64
+ anymorph init
65
+ anymorph doctor
66
+ anymorph prepare <workspaceId>
48
67
  ```
49
68
 
50
- `geo init` installs the bundled skills and local memory scaffold. It does not
51
- create a strategy run. `geo prepare` fetches the workspace run package from the
52
- backend and creates the run files.
69
+ `init` installs or syncs the managed skills and local memory scaffold. It does
70
+ not create a strategy run.
71
+ `prepare` fetches the workspace run package from the backend and creates the
72
+ run files.
53
73
 
54
- `geo prepare` creates:
74
+ `prepare` creates:
55
75
 
56
76
  - `agent/runs/{runId}/manifest.json`
57
77
  - `agent/runs/{runId}/context.json`
78
+ - `agent/runs/{runId}/policy.json`
58
79
  - `agent/runs/{runId}/intents.json`
80
+ - `agent/runs/{runId}/products.json`
81
+ - `agent/runs/{runId}/intent_products.json`
59
82
  - `agent/runs/{runId}/crawl_logs.json`
60
83
  - `agent/runs/{runId}/status.json`
61
84
 
62
85
  `intents.json` comes from `signals.intents` in the backend prepare response.
63
86
  It is the same source used by the remote GEO strategy runner.
87
+ For commerce workspaces, `products.json` and `intent_products.json` come from
88
+ the active Product Catalog and IntentProduct mappings.
64
89
 
65
90
  Inspect prepared intents when needed:
66
91
 
67
92
  ```bash
68
- anymorph geo intents {runId} --repo .
93
+ anymorph intents {runId}
69
94
  ```
70
95
 
71
96
  Then run this skill against that `runId`. Use MCP tools only to enrich the
72
97
  prepared context or inspect deeper evidence. Do not use MCP calls to replace
73
98
  the run package.
74
99
 
75
- For local CLI runs, there is no remote runtime artifact submission tool. The
76
- final artifact contract is the filesystem contract:
100
+ For local runs, there is no hosted runtime artifact submission tool. The final
101
+ artifact contract is the same JSON object shape the remote submit tool accepts,
102
+ but persisted through the filesystem:
77
103
 
104
+ - write `agent/runs/{runId}/subagents/brand_owned.json`
105
+ - write `agent/runs/{runId}/subagents/brand_owned.rationale.md`
106
+ - write `agent/runs/{runId}/subagents/geo_pages.json`
107
+ - write `agent/runs/{runId}/subagents/geo_pages.rationale.md`
108
+ - write `agent/runs/{runId}/subagents/third_party.json`
109
+ - write `agent/runs/{runId}/subagents/third_party.rationale.md`
78
110
  - write `agent/runs/{runId}/actions.json`
79
111
  - write `agent/runs/{runId}/rationale.md`
80
112
  - update `agent/runs/{runId}/status.json`
@@ -82,19 +114,75 @@ final artifact contract is the filesystem contract:
82
114
  After writing artifacts, validate them with:
83
115
 
84
116
  ```bash
85
- anymorph geo validate {runId} --repo .
117
+ anymorph validate {runId}
118
+ ```
119
+
120
+ Then commit and push the tenant repo artifacts, and submit the run to backend:
121
+
122
+ ```bash
123
+ git add agent/runs/{runId} agent/STRATEGY.md agent/LEARNINGS.md
124
+ git commit -m "chore: add geo strategy run {runId}"
125
+ git push origin main
126
+ anymorph submit {runId}
86
127
  ```
87
128
 
129
+ `submit` registers or updates the backend run as `proposed`, reads
130
+ `agent/runs/{runId}/actions.json` from the tenant repo, and immediately
131
+ materializes it in Dashboard. There is no separate approve step.
132
+
133
+ ### Adding Actions After a Run Is Submitted
134
+
135
+ A submitted run is immutable. Once `submit` succeeds the backend locks the run
136
+ as `executed`, so re-submitting the same `runId` — even after editing
137
+ `actions.json` and pushing again — is a silent no-op: it returns `executed`
138
+ without re-reading the file or materializing anything.
139
+
140
+ To add or change actions, do not edit an executed run. Run a fresh `prepare` to
141
+ get a new `runId`, write the actions under that new run directory, then
142
+ `validate`, commit, push, and `submit` the new run.
143
+
144
+ Put only the new actions in the new run unless you intend to re-materialize the
145
+ prior set. Materialize is additive, not a declarative replace: `third_party` and
146
+ `brand_owned` actions create new rows on every run (the prior run's rows stay
147
+ `open`), while `geo_page` actions upsert by slug (the same slug is re-queued for
148
+ generation).
149
+
88
150
  Only use runtime artifact tools when the host environment explicitly provides
89
151
  them. In ordinary local tenant repo work, the filesystem artifacts above are the
90
152
  source of truth.
91
153
 
154
+ ## Contract Guardrails
155
+
156
+ Write `agent/runs/{runId}/actions.json` exactly to the production contract in
157
+ `apps/shared-contracts/src/index.cjs` plus the backend business rules in
158
+ `apps/backend/src/modules/geo-strategy/geo-strategy-action.schema.ts`.
159
+
160
+ - The artifact root is strict. Use only `schemaVersion`, `runId`, `policy`, and `actions`. Never add `artifactPaths` to `actions.json`; that belongs only to submit transport.
161
+ - Each action is strict. Include `id`, `operation`, `surface`, `assetType`, `intentId`, `target`, `objective`, `changeBrief`, `reason`, `expectedOutcome`, `evidence`, `priority`, and `confidence`; do not add extra keys.
162
+ - Each `evidence[]` item is strict: `{ "type": "signal" | "workspace" | "source" | "research", "ref": "...", "summary": "..." }`.
163
+ - `target` is strict. Allowed keys are only `pageId`, `url`, `title`, `description`, `queryCluster`, `sourceDomain`, `suggestedSlug`, `pageRole`, and `targetProductIds`. Never use `target.domain`; use `target.sourceDomain` for third-party domains.
164
+ - `brand_owned` target may use `pageId` or `url`; do not include `pageRole`, `suggestedSlug`, `sourceDomain`, or `targetProductIds`.
165
+ - `geo_page` create target must use root-relative `suggestedSlug` or `url` plus `title`; do not include `pageId` or `sourceDomain`.
166
+ - `geo_page` update target may use `pageId` or `url`; do not include `pageRole`, `suggestedSlug`, `sourceDomain`, or `targetProductIds`.
167
+ - `third_party` target must use `sourceDomain` or absolute http(s) `url`; do not include `pageId`, `pageRole`, `suggestedSlug`, or `targetProductIds`.
168
+
92
169
  ## Final Decision Rules
93
170
 
94
171
  - Emit at most 20 actions.
95
172
  - Use only `create` or `update` operations.
173
+ - Use `surface: "on_page"` for `brand_owned` and `geo_page`.
174
+ - Use `surface: "off_page"` for `third_party`.
96
175
  - Use `brand_owned` only for customer-owned discovered pages.
176
+ - Use `brand_owned` only with `operation: "update"`.
97
177
  - Use `geo_page` only for generated or managed GEO page work.
98
178
  - Use `third_party` only for off-page Earn work.
99
179
  - Every action must include either an existing `intentId` or a non-null `proposedIntent`.
180
+ - If `intentId` is a string, omit `proposedIntent`; do not set it to null.
181
+ - `geo_page` create actions must include a root-relative `target.suggestedSlug` or `target.url` and `target.title`.
182
+ - For commerce workspace `geo_page` create actions, include 1-5 `target.targetProductIds` from `products.json` or `intent_products.json`.
183
+ - Do not emit commerce `geo_page` create actions when no catalog product evidence fits the intent.
100
184
  - Third-party actions must name the exact domain and, when known, the exact publish surface.
185
+ - Third-party `changeBrief` must include the surface, format, angle, and concrete execution steps.
186
+ - Third-party actions for directory, review, listing, marketplace, partner, open-listicle, social, or owned-profile work should be based on `$third-party-execution-planning` output when the diagnosis proposal is selected.
187
+ - When using `$third-party-execution-planning`, derive the final action from its work-order fields: `executionType`, `surfaces`, `requiredAssets`, `policyChecks`, `executionSteps`, `completionSignal`, `impactSignal`, and `fallback`.
188
+ - For third-party final actions, fold `executionType`, required assets, policy checks, operator steps, and fallback into `changeBrief`; use `completionSignal` and `impactSignal` to write `expectedOutcome`.
@@ -19,6 +19,7 @@ It is the final decision-maker for the run.
19
19
 
20
20
  - `agent/runs/{runId}/context.json`
21
21
  - `agent/runs/{runId}/manifest.json`
22
+ - `agent/runs/{runId}/policy.json`
22
23
  - `agent/runs/{runId}/intents.json`
23
24
  - `agent/BRAND.md`
24
25
  - `agent/STRATEGY.md`
@@ -104,11 +105,45 @@ It should include:
104
105
  - relevant brand constraints
105
106
  - strategy priorities from `STRATEGY.md`
106
107
  - last-week performance summary
107
- - recent action exclusion mask
108
+ - recent action exclusion mask (derived from `signals.priorDecisions` — see below)
108
109
  - candidate intent/page/domain slices
109
110
  - evidence budget
110
111
  - output schema
111
112
 
113
+ #### Building the exclusion mask
114
+
115
+ The authoritative source is the top-level `signals.priorDecisions[]` array — it holds every active decision (intent-anchored, url-anchored, AND sourceDomain-anchored, including domain-only third-party rejections that have no intent/page to attach to). Match candidates against this array.
116
+
117
+ `signals.intents[].priorDecisions` and `signals.pages[].priorDecisions` are a pre-grouped convenience mirror (the same decisions, attached to the intent/page they anchor on) — use them as a shortcut, but do not rely on them as the only source, since domain-only decisions appear only in the top-level array.
118
+
119
+ A prior decision is a match when its `match` object aligns with the candidate on all present fields:
120
+
121
+ ```
122
+ match key = { intentId, assetType, operation, target }
123
+ ```
124
+
125
+ For each candidate, look up matches in `signals.priorDecisions[]` by comparing:
126
+
127
+ - the candidate's resolved `intentId` against the intent the decision is attached to (via `signals.intents[].priorDecisions`), and
128
+ - the decision's `match` fields against the candidate: `assetType`, `operation`, and the `target` fields `url`, `queryCluster`, `sourceDomain`, or `suggestedSlug`.
129
+
130
+ Match on any non-null subset (a domain-level third-party decision matches a candidate that targets the same `sourceDomain`).
131
+
132
+ Apply the mask as follows:
133
+
134
+ - **`reasonClass: "durable_exclusion"` with `n ≥ 2`** — suppress the candidate entirely.
135
+ Do not pass it to any channel diagnosis.
136
+ Record in `rationale.md`: the intentId/target, `reasonClass`, `reasonTags`, the human `reason` text (if present), and `n` (number of times dismissed/rejected).
137
+
138
+ - **`reasonClass: "fixable_critique"` (any `n`)** — keep the candidate but pass the prior critique to the channel diagnosis as a constraint.
139
+ The diagnosis must address the critique (e.g. revise tone, adjust value proposition, change timing) rather than reproducing the previously rejected form.
140
+ Record in the channel rationale that the candidate was revised to address a prior critique, citing `reasonTags` and `reason`.
141
+
142
+ - **`reasonClass: "durable_exclusion"` with `n = 1`** — treat as a soft signal.
143
+ Pass the candidate with a note that it was dismissed once; the channel diagnosis may still include it if evidence is strong, but must acknowledge the prior dismissal in its rationale.
144
+
145
+ If `priorDecisions` is absent or empty for an intent, proceed normally.
146
+
112
147
  This context is shared by all three channel diagnoses.
113
148
 
114
149
  Channel diagnoses may receive channel-specific candidate slices, but they should share the same strategic baseline.
@@ -120,8 +155,28 @@ Apply these channel skills directly:
120
155
  - `brand-owned-diagnosis`
121
156
  - `geo-pages-diagnosis`
122
157
  - `third-party-diagnosis`
158
+ - `third-party-execution-planning`
159
+ - `seo-opportunity-audit`
160
+ - `seo-technical-diagnosis`
161
+ - `seo-page-diagnosis`
162
+ - `seo-serp-opportunity-research`
163
+ - `seo-internal-link-opportunity`
164
+ - `seo-ecommerce-opportunity`
123
165
 
124
166
  Each channel diagnosis returns one compact proposal set to the lead agent.
167
+ SEO diagnosis skills return supporting evidence and action candidates under
168
+ `agent/seo/{skillName}/{runId}/`. Use those artifacts to support final GEO
169
+ actions, but the lead agent still owns final action selection and submission.
170
+ Persist every channel diagnosis before final judging:
171
+
172
+ - `agent/runs/{runId}/subagents/brand_owned.json`
173
+ - `agent/runs/{runId}/subagents/brand_owned.rationale.md`
174
+ - `agent/runs/{runId}/subagents/geo_pages.json`
175
+ - `agent/runs/{runId}/subagents/geo_pages.rationale.md`
176
+ - `agent/runs/{runId}/subagents/third_party.json`
177
+ - `agent/runs/{runId}/subagents/third_party.rationale.md`
178
+
179
+ The JSON files are the mergeable source of truth. The rationale files should be compact decision logs: accepted, rejected, uncertain, missing evidence, and cross-channel concerns. Do not paste raw transcripts.
125
180
 
126
181
  Expected channel diagnosis output:
127
182
 
@@ -143,7 +198,7 @@ Expected channel diagnosis output:
143
198
  "evidence": ["Evidence summary"]
144
199
  }
145
200
  ],
146
- "actionFamily": "refresh_existing_page",
201
+ "actionFamily": "update_existing_page",
147
202
  "operations": ["Add proof section"],
148
203
  "priority": "high",
149
204
  "confidence": "medium",
@@ -171,18 +226,59 @@ Expected channel diagnosis output:
171
226
  Channel diagnoses must not include `workspaceId`, `runId`, `channel`, final action id, final `assetType`, or final URL.
172
227
  The orchestrator derives final action ids, channel-to-asset mapping, and executable action shape.
173
228
 
229
+ For selected `third_party` proposals, apply `third-party-execution-planning`
230
+ before final action writing. Diagnosis decides whether an external opportunity
231
+ is worth considering; execution planning turns the selected opportunity into a
232
+ human-executable plan for social surfaces and non-social surfaces such as
233
+ directories, review sites, marketplaces, partner pages, open listicles,
234
+ editorial inclusion pages, and owned third-party profiles.
235
+
174
236
  ### 5. Judge Channel Outputs
175
237
 
176
- The orchestrator reviews all proposals.
238
+ The orchestrator reads the three persisted channel JSON files and rationale files, then reviews all proposals.
177
239
 
178
240
  It should ask:
179
241
 
180
242
  - Does this proposal match tenant strategy?
181
243
  - Is the evidence strong enough?
182
244
  - Is it redundant with another channel's proposal?
183
- - Is it suppressed by recent actions?
245
+ - Is it suppressed by recent actions? (see priorDecision check below)
184
246
  - Is the action executable by the current product surface?
185
247
  - Is it more important than competing proposals this week?
248
+ - For generated-page updates, is the page old enough to evaluate, indexed, or backed by a concrete non-performance defect?
249
+
250
+ #### priorDecision check (per proposal)
251
+
252
+ For each proposal returned by a channel diagnosis, derive its match key:
253
+
254
+ ```
255
+ { intentId, assetType, operation, target: { url?, queryCluster?, sourceDomain?, suggestedSlug? } }
256
+ ```
257
+
258
+ Look up matching entries in the authoritative top-level `signals.priorDecisions[]` array. A decision matches when its `match` object aligns with the proposal on all present fields — `assetType`, `operation`, and the `target` fields `url`, `queryCluster`, `sourceDomain`, or `suggestedSlug` (a domain-only third-party decision matches a proposal targeting the same `sourceDomain`), plus the `intentId` the decision is attached to (via `signals.intents[].priorDecisions`). The `signals.intents[].priorDecisions` and `signals.pages[].priorDecisions` attachments are a convenience shortcut for intent- and url-anchored proposals; the top-level array is the source of truth and the only place domain-only decisions appear.
259
+
260
+ Apply the following rules and record the outcome in `rationale.md`:
261
+
262
+ 1. **Suppress** — if any matching prior decision has `reasonClass: "durable_exclusion"` and `n ≥ 2`:
263
+ reject the proposal; do not emit it as a final action.
264
+ In `rationale.md` write: `SUPPRESSED — durable exclusion: <reasonTags> (n=<n>). Prior reason: "<reason>".`
265
+
266
+ 2. **Revise** — if any matching prior decision has `reasonClass: "fixable_critique"`:
267
+ keep the proposal but require that the final action explicitly addresses the critique.
268
+ Update `changeBrief` and `objective` to reflect the correction.
269
+ In `rationale.md` write: `REVISED — fixable critique: <reasonTags>. Addressed by: <description of correction>.`
270
+
271
+ 3. **Soft flag** — if any matching prior decision has `reasonClass: "durable_exclusion"` and `n = 1`:
272
+ the proposal may proceed, but record: `SOFT FLAG — dismissed once: <reasonTags>. Proceeding because <reason for override>.`
273
+
274
+ 4. **No prior decisions** — proceed normally; no rationale entry required for this check.
275
+
276
+ Fresh generated-page guard:
277
+
278
+ - If a generated page was published less than 7 days ago, do not emit a `geo_page` update based only on low visibility, zero traffic, zero GSC impressions, or zero AI citations.
279
+ - For fresh generated pages, accept an update only when the proposal identifies a concrete non-performance defect: wrong intent, broken route, crawl/indexation blocker, duplicate/cannibalization, missing brand/entity clarity, or factual/product-claim defect.
280
+ - If a generated page is not indexed after 7 days, keep the issue in analysis and flag it as indexation/technical evidence. Prefer a technical follow-up or monitor note over a content update unless the content itself is also defective.
281
+ - When rejecting a premature generated-page update, record the page id/URL and the next reasonable evaluation window in `rationale.md` or the channel rationale.
186
282
 
187
283
  The orchestrator may:
188
284
 
@@ -225,11 +321,20 @@ Guardrail:
225
321
  - `brand_owned` is only for customer-owned discovered pages.
226
322
  - Do not send Anymorph-generated, recommended, preview-only, or unpublished GEO pages to Fix.
227
323
  - Existing generated page work must stay `assetType: "geo_page"` even when the proposed operation is a technical fix or content iteration.
324
+ - Do not emit a `geo_page` update for a generated page published less than 7 days ago unless there is a concrete non-performance defect.
325
+ - Do not treat missing performance data on a fresh generated page as proof of weakness.
326
+ - Do flag a generated page that is not indexed after 7 days, but keep indexation analysis separate from content iteration unless both are supported by evidence.
228
327
 
229
328
  ### 8. Write Artifacts
230
329
 
231
330
  Write:
232
331
 
332
+ - `agent/runs/{runId}/subagents/brand_owned.json`
333
+ - `agent/runs/{runId}/subagents/brand_owned.rationale.md`
334
+ - `agent/runs/{runId}/subagents/geo_pages.json`
335
+ - `agent/runs/{runId}/subagents/geo_pages.rationale.md`
336
+ - `agent/runs/{runId}/subagents/third_party.json`
337
+ - `agent/runs/{runId}/subagents/third_party.rationale.md`
233
338
  - `agent/runs/{runId}/actions.json`
234
339
  - `agent/runs/{runId}/rationale.md`
235
340
  - `agent/runs/{runId}/status.json`
@@ -249,12 +354,29 @@ must not edit it. If a brand fact looks wrong, raise it in
249
354
 
250
355
  ## Final Action Contract
251
356
 
252
- Each final action must include:
357
+ Write `agent/runs/{runId}/actions.json` to the exact production contract.
358
+ The Zod source of truth is `apps/shared-contracts/src/index.cjs`; the
359
+ asset-type target business rules are in
360
+ `apps/backend/src/modules/geo-strategy/geo-strategy-action.schema.ts`.
361
+
362
+ The artifact root is strict. It must contain only:
253
363
 
364
+ - `schemaVersion: 1`
365
+ - `runId`
366
+ - `policy`, matching `agent/runs/{runId}/policy.json`
367
+ - `actions`, with at most 20 items
368
+
369
+ Do not add root-level helper fields such as `artifactPaths`. `artifactPaths`
370
+ belongs only to submit transport, never to `actions.json`.
371
+
372
+ Each final action is strict. It must contain only:
373
+
374
+ - `id`
254
375
  - `operation`
255
376
  - `surface`
256
377
  - `assetType`
257
378
  - `intentId`
379
+ - `proposedIntent`, only when `intentId` is null
258
380
  - `target`
259
381
  - `objective`
260
382
  - `changeBrief`
@@ -264,6 +386,50 @@ Each final action must include:
264
386
  - `priority`
265
387
  - `confidence`
266
388
 
389
+ Each `evidence[]` item is strict:
390
+
391
+ - `type`: one of `signal`, `workspace`, `source`, `research`
392
+ - `ref`: non-empty string
393
+ - `summary`: non-empty string
394
+
395
+ `target` is strict. Allowed target keys are only:
396
+
397
+ - `pageId`
398
+ - `url`
399
+ - `title`
400
+ - `description`
401
+ - `queryCluster`
402
+ - `sourceDomain`
403
+ - `suggestedSlug`
404
+ - `pageRole`
405
+ - `targetProductIds`
406
+
407
+ Never use `target.domain`. For external domains, use `target.sourceDomain`.
408
+
409
+ Additional action rules:
410
+
411
+ - `brand_owned` uses `operation: "update"` and `surface: "on_page"`.
412
+ - `brand_owned` target must include `pageId` or `url`; do not include `pageRole`, `suggestedSlug`, `sourceDomain`, or `targetProductIds`.
413
+ - `geo_page` uses `surface: "on_page"`.
414
+ - `third_party` uses `surface: "off_page"`.
415
+ - If `intentId` is a string, omit `proposedIntent`.
416
+ - If `intentId` is null, include `proposedIntent.name` and `proposedIntent.reason`.
417
+ - `geo_page` create actions must include a root-relative `target.suggestedSlug` or `target.url`, plus `target.title`.
418
+ - `geo_page` create target must not include `pageId` or `sourceDomain`.
419
+ - `geo_page` update target must include `pageId` or `url`; do not include `pageRole`, `suggestedSlug`, `sourceDomain`, or `targetProductIds`.
420
+ - For commerce `geo_page` create actions, use `target.targetProductIds` only from product evidence in `products.json`, `intent_products.json`, or `get_intent_products`.
421
+ - Third-party `changeBrief` must name the publish surface, content format, content angle, and concrete execution steps.
422
+ - Third-party target must include `sourceDomain` or absolute http(s) `url`; do not include `pageId`, `pageRole`, `suggestedSlug`, or `targetProductIds`.
423
+ - Third-party `target.url` should be the exact execution surface when known: social thread/channel/profile, directory listing or submission page, review profile/category page, marketplace or partner page, open listicle/editorial page, or owned third-party profile.
424
+ - Third-party `operation` should be `create` for new external presence, inclusion requests, review collection motions, or owned-channel content; use `update` for existing listing/profile refreshes or factual corrections.
425
+ - Third-party `expectedOutcome` should name one observable result: listing/profile update, submission acknowledgement, review motion launched, editorial acknowledgement/update, creator mention, indexed post, or citation movement.
426
+ - When a `third_party` action uses `third-party-execution-planning`, derive final fields from the work order:
427
+ - `target.url` from the most actionable `surfaces[].url`.
428
+ - `target.sourceDomain` from that surface URL's domain.
429
+ - `operation` from `executionType` and the decision matrix.
430
+ - `changeBrief` from `executionType`, `whyThisSurface`, `requiredAssets`, `policyChecks`, `executionSteps`, and `fallback`.
431
+ - `expectedOutcome` from `completionSignal` plus `impactSignal`.
432
+
267
433
  ## Guardrails
268
434
 
269
435
  - Do not let a channel diagnosis proposal pass through automatically.