From 1f9fea286ed49b87c829e42c6f2c7eb5918fc6e2 Mon Sep 17 00:00:00 2001 From: rita-aga Date: Thu, 18 Jun 2026 11:45:03 -0400 Subject: [PATCH] fix(foresight): port explicit file-write prompts --- .../2026-06-18-arn-66-foresight-port.md | 142 ++++++++++++++++++ .../paw-foresight/agents/adversary/AGENT.md | 14 +- os-apps/paw-foresight/agents/dweller/AGENT.md | 12 +- .../agents/endpoint-writer/AGENT.md | 22 ++- .../paw-foresight/agents/repairer/AGENT.md | 14 +- .../paw-foresight/agents/surveyor/AGENT.md | 14 +- .../wasm/animate_dwellers/src/lib.rs | 55 ++++++- .../wasm/render_artifacts/src/lib.rs | 32 +++- .../wasm/sample_endpoints/src/lib.rs | 31 +++- .../paw-foresight/wasm/seed_world/src/lib.rs | 41 ++++- .../wasm/spawn_adversaries/src/lib.rs | 33 +++- .../wasm/spawn_repairers/src/lib.rs | 33 +++- 12 files changed, 407 insertions(+), 36 deletions(-) create mode 100644 docs/proofs/2026-06-18-arn-66-foresight-port.md diff --git a/docs/proofs/2026-06-18-arn-66-foresight-port.md b/docs/proofs/2026-06-18-arn-66-foresight-port.md new file mode 100644 index 000000000..3b571d371 --- /dev/null +++ b/docs/proofs/2026-06-18-arn-66-foresight-port.md @@ -0,0 +1,142 @@ +# ARN-66 Foresight Port And Topology Proof + +Date: 2026-06-18 + +## Scope + +TemperPaw worktree: `/Users/seshendranalla/.codex/worktrees/0be7/temperpaw` +Branch: `codex/arn-66-foresight-port` +Remote: `origin=https://github.com/nerdsane/temperpaw.git` + +This proof covers the ARN-66 port/readiness lane for the Deep Sci-Fi/Foresight engine: + +- confirm canonical deployment topology; +- check whether searched-corridor fixes landed only in openpaw or also in canonical Foresight; +- port the prompt/file-write recipe delta to GitHub main; +- avoid duplicating ARN-65's broader self-heal lane. + +This is not a fresh productive end-to-end run proof. It prepares the path for one. + +## Deployment Topology Evidence + +Railway project `openpaw-seshendranalla` contains both services: + +- `openpaw` service id `4a8dedaa-8a2e-4cdd-945b-e06c781bb3f0` +- `foresight` service id `ed6c91b5-d235-4286-964a-41acdab47c49` + +Canonical Foresight evidence: + +- Railway service config source image: `ghcr.io/nerdsane/temperpaw:sha-e1c3968` +- latest deployment: `5110497a-...`, SUCCESS at `2026-06-18 13:21:41 UTC` +- database host: `aws-1-us-west-1.pooler.supabase.com` +- `TEMPER_POSTGRES_MAX_CONNECTIONS=40` +- `DD_SERVICE=foresight` +- `DD_DBM_DATABASE_SERVICE=foresight-supabase` +- `LLM_PROVIDER=openai_codex` +- `LLM_MODEL=gpt-5.5` +- public readiness: `https://foresight-production-72d1.up.railway.app/readyz` returned ready +- Genesis bootstrap ref includes `temperpaw/paw-foresight@659e40c663024af1acf1ed6d2a39d872d3dbdf14` + +Openpaw comparison evidence: + +- Railway service config source image: `ghcr.io/nerdsane/temperpaw:sha-5c3c05f` +- latest deployment: `b1b96383-...`, SUCCESS at `2026-06-18 15:23:06 UTC` +- database host: `postgres.railway.internal` +- `DD_SERVICE=temperpaw` +- `DD_DBM_DATABASE_SERVICE=temperpaw-postgres` +- `LLM_PROVIDER=openai_codex` +- `LLM_MODEL=gpt-5.5` +- Genesis bootstrap ref includes `temperpaw/paw-foresight@01ac826b9604ef1828eee146724a44953375ebfb` + +Conclusion: canonical deployment for this run track is `foresight` on Supabase, not `openpaw` on Railway Postgres. Both checked deployments are still configured for Codex/OpenAI provider `openai_codex` with model `gpt-5.5`; no provider switch is part of this work. + +## Commit And Genesis Evidence + +Local Git evidence: + +- `e3f2fbaf fix(foresight): give session agents an explicit file-write recipe (no API-guessing)` is not an ancestor of `origin/main`. +- `22795542 fix(foresight): self-heal the endpoint writer phase (Endpoint.Sampled)` is not an ancestor of `origin/main`. +- both are present on the searched-corridor branch, not GitHub main. + +Genesis evidence: + +- canonical `paw-foresight@659e40c663024af1acf1ed6d2a39d872d3dbdf14` has 90 files and includes `wasm/sample_endpoints/src/lib.rs`, `specs/endpoint.ioa.toml`, and the other corridor WASM modules. +- canonical `paw-foresight@659e40...` contains Endpoint self-heal markers in `specs/endpoint.ioa.toml`, including `Sampled`, `ResumeWriter`, `state_timeout`, `allow_indefinite_states`, and `UnderRepair`. +- canonical `paw-foresight@659e40...` also contains earlier file-write guidance in WASM prompt source, but still contains the bare `` placeholder in all six checked WASM prompt modules. +- openpaw `paw-foresight@01ac826b9604ef1828eee146724a44953375ebfb` has 29 files and does not include the checked `paw-foresight` WASM/spec paths, so it is not the canonical app bundle for this lane. + +Conclusion: the self-heal class from `22795542` is already visible in the canonical deployed Genesis ref, while GitHub main is missing it. ARN-65 is actively owning the broader self-heal source convergence. This ARN-66 branch therefore ports only the prompt/file-write delta, with a tighter tested recipe than the deployed `659e40...` bundle currently exposes. + +## Red Test + +Added failing contract tests first in these modules: + +- `sample_endpoints`: `writer_prompt_gives_explicit_file_write_recipe` +- `seed_world`: `surveyor_prompt_gives_explicit_file_write_recipe` +- `spawn_adversaries`: `adversary_prompt_gives_explicit_file_write_recipe` +- `spawn_repairers`: `repairer_prompt_gives_explicit_file_write_recipe` +- `render_artifacts`: `author_prompt_gives_explicit_file_write_recipe` +- `animate_dwellers`: `dweller_prompt_gives_explicit_file_write_recipe` + +Initial targeted runs failed because GitHub main did not show literal `temper.write("/...md", ...)` calls in the executable prompts and left the ambiguous file-id placeholder. + +## Green Change + +Executable prompts now tell agents: + +- the workspace already exists; +- `temper.write` is the only file creation path; +- not to create Files, Directories, or Workspaces manually; +- the exact write path to call for each phase; +- that `temper.write` returns `file_id`, `path`, and `workspace_id`; +- to pass `result["file_id"]` into the relevant completion action. + +Updated agent manuals for surveyor, endpoint writer, adversary, repairer, and dweller to match. The endpoint-writer manual was also aligned from stale `SubmitForRepair` language to the current `BundleWritten` diversity-gate contract. + +## Automated Verification + +Commands completed successfully after implementation: + +```sh +cargo test --manifest-path os-apps/paw-foresight/wasm/sample_endpoints/Cargo.toml +cargo test --manifest-path os-apps/paw-foresight/wasm/seed_world/Cargo.toml +cargo test --manifest-path os-apps/paw-foresight/wasm/spawn_adversaries/Cargo.toml +cargo test --manifest-path os-apps/paw-foresight/wasm/spawn_repairers/Cargo.toml +cargo test --manifest-path os-apps/paw-foresight/wasm/render_artifacts/Cargo.toml +cargo test --manifest-path os-apps/paw-foresight/wasm/animate_dwellers/Cargo.toml +git diff --check +``` + +Results: + +- `sample_endpoints`: 11 passed +- `seed_world`: 7 passed +- `spawn_adversaries`: 8 passed +- `spawn_repairers`: 13 passed +- `render_artifacts`: 7 passed +- `animate_dwellers`: 7 passed +- whitespace check passed + +## Deep Sci-Fi UI Status + +Read-only inspection found the run-progress UI on local Deep Sci-Fi branch `codex/dsf-2`, not on the deployed Railway service: + +- repo: `/Users/seshendranalla/Development/deep-sci-fi-worktrees/dsf-2` +- branch: `codex/dsf-2`, ahead of `origin/codex/dsf-2` by 2 commits +- commits: `dfe5a904 Add live run progress UI with stall detection`, `efcc166f Add e2e coverage for live run progress panel` +- changed files include `platform/components/world/RunProgressLive.tsx`, `platform/app/api/world/[id]/run-status/route.ts`, `platform/lib/run-status.ts`, `platform/lib/temper-server.ts`, and e2e/unit coverage. + +The UI path uses `TEMPER_API_URL`, `TEMPER_API_KEY`, and `TEMPER_TENANT` through the server-side helper; no openpaw URL hardcode was found in the run-progress path. The deployed DSF Railway backend is sourced from `arni-labs/deep-sci-fi`, root `platform/backend`, with latest successful deploy `2026-04-04 12:57:10 UTC` at commit `deacc27a...`; its variables do not include the Temper proxy variables needed by the run-progress API. A second DSF repo PR/deploy is required before the run-progress UI can serve as canonical Foresight run proof. + +## ADR Judgment + +No new ADR was added. This change does not alter entity specs, policies, storage, triggers, deployment behavior, or orchestration architecture; it ports and tests prompt/operator guidance for existing actions and tools. + +## Fresh Run Readiness Checklist + +- Merge/publish this prompt-file-write port into the canonical `paw-foresight` Genesis app. +- Let ARN-65 finish and publish the broader source convergence for Endpoint.Sampled, UnderRepair, World.Active, and Seeding self-heal, or explicitly pin to the already deployed canonical `659e40...` behavior if that remains the accepted production baseline. +- Configure and deploy the DSF run-progress UI against canonical Foresight with `TEMPER_API_URL=https://foresight-production-72d1.up.railway.app`, the correct tenant, and a server-side API key. +- Start one fresh productive run against `foresight`/Supabase. +- Capture OData proof for the new world: World state, Endpoint state progression, Sessions activity, file ids written by `temper.write`, and final artifacts/stories. +- Confirm in Datadog under `DD_SERVICE=foresight` and `DD_DBM_DATABASE_SERVICE=foresight-supabase` that the run uses Supabase and does not fall back to openpaw Railway Postgres. diff --git a/os-apps/paw-foresight/agents/adversary/AGENT.md b/os-apps/paw-foresight/agents/adversary/AGENT.md index 12f6467b9..00b68dfdb 100644 --- a/os-apps/paw-foresight/agents/adversary/AGENT.md +++ b/os-apps/paw-foresight/agents/adversary/AGENT.md @@ -33,13 +33,21 @@ In hindcast worlds you have NO web access; attack from the corpus, the skeleton, The API silently drops unknown fields. Use these exact names. -## Self-Reporting Completion +## Writing Your Challenge Log + +`temper.write` is the ONLY way to create a file, and your workspace already exists. Never create Files, Directories, or Workspaces yourself, and never invent a file-creation API through `temper.action` — `temper.write` does the whole job. Call it exactly like this: + +```python +result = temper.write("/challenge-log.md", "...markdown: each attack with its reasoning...") +# result == {"file_id": "...", "path": "...", "workspace_id": "..."} +challenge_log_file_id = result["file_id"] +``` -Write a challenge log with `temper.write` (markdown: each attack with its reasoning), then: +## Self-Reporting Completion ```python temper.action("Paths", "", "ChallengeComplete", { - "challenge_log_file_id": "", + "challenge_log_file_id": challenge_log_file_id, "challenge_flags": '[{"kind": "...", "severity": "...", "note": "..."}]' }) temper.done("complete") diff --git a/os-apps/paw-foresight/agents/dweller/AGENT.md b/os-apps/paw-foresight/agents/dweller/AGENT.md index 8c9518f1b..57308ce30 100644 --- a/os-apps/paw-foresight/agents/dweller/AGENT.md +++ b/os-apps/paw-foresight/agents/dweller/AGENT.md @@ -36,12 +36,16 @@ Your within-frontier calls are graded by the system like everything else in this The API silently drops unknown fields. Use these exact names. +### Writing files + +`temper.write` is the ONLY way to create a FILE, and your workspace already exists. Never create Files, Directories, or Workspaces yourself, and never invent a file-creation API — `temper.create` is for Artifacts only, never for files. Every call returns `{"file_id": "...", "path": "...", "workspace_id": "..."}`; use `result["file_id"]` for the file ids below. + ### Filing a traversal ```python -# After writing your traversal notes with temper.write: +result = temper.write("/traversal-notes.md", "...your lived timeline notes...") temper.action("Dwellers", "", "RecordTraversal", { "path_id": "", - "traversal_note_file_id": "" + "traversal_note_file_id": result["file_id"] }) ``` @@ -62,9 +66,9 @@ artifact = temper.create("Artifacts", { "title": "Short, in-world title", "author_dweller_id": "" }) -# temper.write the full story (markdown), capture the file id, then: +result = temper.write("/story.md", "...the full story, markdown...") temper.action("Artifacts", "", "SubmitForCheck", { - "content_file_id": "", + "content_file_id": result["file_id"], "cited_node_ids": '["", ""]' }) ``` diff --git a/os-apps/paw-foresight/agents/endpoint-writer/AGENT.md b/os-apps/paw-foresight/agents/endpoint-writer/AGENT.md index ec895471f..bd83ccf34 100644 --- a/os-apps/paw-foresight/agents/endpoint-writer/AGENT.md +++ b/os-apps/paw-foresight/agents/endpoint-writer/AGENT.md @@ -11,7 +11,7 @@ World.SampleEndpoints created your Endpoint entity, assigned its driver stance ( - Optionally a corpus file and a driver basis file - Web tools, unless this is a hindcast world -You write one document bundle, submit it for repair, and finish. Repairers will work backward from your documents; their repair costs decide your endpoint's weight — it is earned, not asserted. +You write one document bundle, report it with `BundleWritten`, and finish. The diversity gate decides which bundles move into repair. Repairers will work backward from your documents; their repair costs decide your endpoint's weight — it is earned, not asserted. ## Your Job @@ -22,7 +22,7 @@ Then write a document bundle: 2-4 documents, every one dated AT the target date - a news item, - at least one in-world primary document (a filing, a review, a changelog). -Documents must contain specific dates, named actors, and numbers. Vague futures cannot be repaired. Save the whole bundle as ONE markdown file with `temper.write`. +Documents must contain specific dates, named actors, and numbers. Vague futures cannot be repaired. Save the whole bundle as ONE markdown file with `temper.write` (see below) — your workspace already exists; never create Files, Directories, or Workspaces yourself. In hindcast worlds you have NO web access by design, and you never reference anything dated after the world's vantage. @@ -30,18 +30,28 @@ In hindcast worlds you have NO web access by design, and you never reference any The API silently drops unknown fields. Use these exact names. +## Writing Your Bundle + +`temper.write` is the ONLY way to create a FILE, and your workspace already exists. Never create Files, Directories, or Workspaces yourself, and never invent a file-creation API through `temper.action`. Call it exactly like this: + +```python +result = temper.write("/bundle.md", "...the full markdown bundle...") +# result == {"file_id": "...", "path": "...", "workspace_id": "..."} +bundle_file_id = result["file_id"] +``` + ## Self-Reporting Completion ```python -temper.action("Endpoints", "", "SubmitForRepair", { - "bundle_file_id": "", +temper.action("Endpoints", "", "BundleWritten", { + "bundle_file_id": bundle_file_id, "summary": "", "author_agent_id": "" }) temper.done("complete") ``` -This is critical — SubmitForRepair is what spawns the repairer. An unwritten or unreported bundle is a dead endpoint. +This is critical — `BundleWritten` parks the endpoint for the diversity gate. An unwritten or unreported bundle is a dead endpoint. ## Principles @@ -49,5 +59,5 @@ This is critical — SubmitForRepair is what spawns the repairer. An unwritten o - Hold your assigned stance — the spread is the point; don't drift back to consensus. - Never contradict a determined node. - Specifics or nothing: dates, named actors, numbers. -- One markdown file, one SubmitForRepair, then temper.done. +- One markdown file, one BundleWritten, then temper.done. - You will not repair your own endpoint — repairers are always distinct agents (Cedar enforces it). diff --git a/os-apps/paw-foresight/agents/repairer/AGENT.md b/os-apps/paw-foresight/agents/repairer/AGENT.md index a7ae25115..ce2232989 100644 --- a/os-apps/paw-foresight/agents/repairer/AGENT.md +++ b/os-apps/paw-foresight/agents/repairer/AGENT.md @@ -44,13 +44,21 @@ temper.create("EventNodes", { }) ``` -## Self-Reporting Completion +## Writing Your Repair Log + +`temper.write` is the ONLY way to create a FILE, and your workspace already exists. Never create Files, Directories, or Workspaces yourself, and never invent a file-creation API — `temper.create` is for EventNodes only, never for files. Call `temper.write` exactly like this: + +```python +result = temper.write("/repair-log.md", "...markdown: the backward chain with your reasoning...") +# result == {"file_id": "...", "path": "...", "workspace_id": "..."} +repair_log_file_id = result["file_id"] +``` -Write a repair log with `temper.write` (markdown: the backward chain with your reasoning), then: +## Self-Reporting Completion ```python temper.action("Paths", "", "RepairComplete", { - "repair_log_file_id": "", + "repair_log_file_id": repair_log_file_id, "required_node_ids": '["", ...]', "cost_flags": '[{"kind": "...", "severity": "...", "note": "..."}]' }) diff --git a/os-apps/paw-foresight/agents/surveyor/AGENT.md b/os-apps/paw-foresight/agents/surveyor/AGENT.md index 312a1b594..72ffa051a 100644 --- a/os-apps/paw-foresight/agents/surveyor/AGENT.md +++ b/os-apps/paw-foresight/agents/surveyor/AGENT.md @@ -36,14 +36,24 @@ temper.create("EventNodes", { }) ``` +## Writing Your Skeleton Summary + +`temper.write` is the ONLY way to create a FILE, and your workspace already exists. Never create Files, Directories, or Workspaces yourself, and never invent a file-creation API — `temper.create` is for EventNodes only, never for files. Call it exactly like this: + +```python +result = temper.write("/skeleton.md", "...one-page skeleton summary, markdown...") +# result == {"file_id": "...", "path": "...", "workspace_id": "..."} +graph_snapshot_file_id = result["file_id"] +``` + ## Self-Reporting Completion -Write a one-page skeleton summary with `temper.write` (markdown), then report to the World before finishing: +Report to the World before finishing: ```python temper.action("Worlds", "", "SeedComplete", { "skeleton_node_count": "", - "graph_snapshot_file_id": "" + "graph_snapshot_file_id": graph_snapshot_file_id }) temper.done("complete") ``` diff --git a/os-apps/paw-foresight/wasm/animate_dwellers/src/lib.rs b/os-apps/paw-foresight/wasm/animate_dwellers/src/lib.rs index de86030fa..1ad426ac1 100644 --- a/os-apps/paw-foresight/wasm/animate_dwellers/src/lib.rs +++ b/os-apps/paw-foresight/wasm/animate_dwellers/src/lib.rs @@ -187,10 +187,16 @@ fn dweller_prompt( LIVE the timeline: walk those events as your own biography. Where were you when \ each happened? What did each cost or give you? Be concrete — your employer, your \ money, your tools, your people.\n\n\ - 1. Record your traversal: write your lived timeline notes with temper.write \ - (markdown), then:\n\ + The temper.write tool is the ONLY way to create a FILE, and your workspace already \ + exists. Do NOT create Files, Directories, or Workspaces yourself, and do NOT invent a \ + file-creation API: temper.create is for Artifacts only, never for files. Each \ + temper.write call returns {{\"file_id\": \"...\", \"path\": \"...\", \"workspace_id\": \ + \"...\"}} — use result[\"file_id\"] for the file ids below.\n\n\ + 1. Record your traversal: write your lived timeline notes (markdown) like this:\n\ + result = temper.write(\"/traversal-notes.md\", \"\")\n\ + then:\n\ temper.action(\"Dwellers\", \"{dweller_id}\", \"RecordTraversal\", {{\"path_id\": \ - \"{canonical_path_id}\", \"traversal_note_file_id\": \"\"}})\n\ + \"{canonical_path_id}\", \"traversal_note_file_id\": \"\"}})\n\ 2. If — and only if — some event CANNOT be lived coherently from your vantage (it \ contradicts another event, your incentives, or simple arithmetic of your life), \ file it honestly:\n\ @@ -199,12 +205,14 @@ fn dweller_prompt( above>\", \"note\": \"\"}})\n\ 3. Write YOUR STORY: first person, 600-1200 words, a specific day or episode of \ your life inside this world — not a summary of the timeline. Ground every \ - world-fact in the claims and events above. Save it with temper.write (markdown), \ - then create the artifact and submit it to the gate:\n\ + world-fact in the claims and events above. Save it with temper.write like this:\n\ + result = temper.write(\"/story.md\", \"\")\n\ + then create the artifact (using result[\"file_id\"] as content_file_id) and submit it \ + to the gate:\n\ temper.create(\"Artifacts\", {{\"world_id\": \"{world_id}\", \"path_id\": \ \"{canonical_path_id}\", \"kind\": \"story\", \"title\": \"\", \ \"author_dweller_id\": \"{dweller_id}\", \"author_agent_id\": \"{agent_id}\", \ - \"content_file_id\": \"\", \"cited_node_ids\": \ + \"content_file_id\": \"\", \"cited_node_ids\": \ \"[\\\"\\\", ...]\"}})\n\ temper.action(\"Artifacts\", \"\", \"SubmitForCheck\", {{}})\n\ Then call temper.done(\"complete\")." @@ -949,6 +957,41 @@ mod tests { assert!(!p.contains("temper.read("), "context is inlined, never read"); } + #[test] + fn dweller_prompt_gives_explicit_file_write_recipe() { + let p = dweller_prompt( + "d-1", + "w-1", + "After the Toolmakers", + "2045-06-11", + "Name: Mara", + "- [c-1] reachable: claim text", + "- [n-1] by 2027-01-01: telemetry ships", + "p-9", + "a-1", + ); + assert!( + p.contains("temper.write(\"/traversal-notes.md\""), + "dweller prompt must show the literal temper.write call for traversal notes" + ); + assert!( + p.contains("temper.write(\"/story.md\""), + "dweller prompt must show the literal temper.write call for the story" + ); + assert!( + p.contains("\"file_id\""), + "dweller prompt must name the file_id return field" + ); + assert!( + p.contains("Do NOT") && p.contains("Directories"), + "dweller prompt must forbid improvising Directories file creation" + ); + assert!( + !p.contains(""), + "the bare placeholder must be gone: the recipe captures result[\"file_id\"]" + ); + } + #[test] fn personas_parse_capped_and_named() { let raw = r#"[{"name": "Mara", "role": "engineer"}, {"name": ""}, {"role": "nameless"}, diff --git a/os-apps/paw-foresight/wasm/render_artifacts/src/lib.rs b/os-apps/paw-foresight/wasm/render_artifacts/src/lib.rs index 4c546be08..07805070e 100644 --- a/os-apps/paw-foresight/wasm/render_artifacts/src/lib.rs +++ b/os-apps/paw-foresight/wasm/render_artifacts/src/lib.rs @@ -94,13 +94,20 @@ fn author_prompt( it rests on.\n\ (b) kind \"document\" — the single best in-world document from the bundle, polished, \ dated at the target date ({target_date}).\n\n\ + temper.write is the ONLY way to create a FILE, and your workspace already exists. Do \ + NOT create Files, Directories, or Workspaces yourself, and do NOT invent a \ + file-creation API via temper.action: temper.create is for Artifacts only, never for \ + files. temper.write returns {{\"file_id\": \"...\", \"path\": \"...\", \ + \"workspace_id\": \"...\"}}.\n\n\ For each artifact, in order:\n\ 1. temper.create(\"Artifacts\", {{\"world_id\": \"{world_id}\", \"path_id\": \ \"{path_id}\", \"kind\": \"brief|document\", \"title\": \"...\", \ \"author_agent_id\": \"{{AGENT_ID}}\"}}) — capture the returned artifact id.\n\ - 2. temper.write the full content (markdown) — capture the returned file id.\n\ + 2. Write the full content (markdown) with a distinct path per artifact, e.g.:\n\ + result = temper.write(\"/artifact.md\", \"\")\n\ + Use result[\"file_id\"] as content_file_id below.\n\ 3. temper.action(\"Artifacts\", \"\", \"SubmitForCheck\", \ - {{\"content_file_id\": \"\", \"cited_node_ids\": \"[\\\"...\\\"]\"}})\n\n\ + {{\"content_file_id\": \"\", \"cited_node_ids\": \"[\\\"...\\\"]\"}})\n\n\ Every factual sentence should trace to a cited node — the consistency gate will \ check every citation. Do not publish; submission is where your authority ends.\n\n\ Then call temper.done(\"complete\")." @@ -434,6 +441,27 @@ mod tests { assert!(!p.contains("PassCheck")); } + #[test] + fn author_prompt_gives_explicit_file_write_recipe() { + let p = author_prompt("w-1", "p-9", "file-log", "file-bundle", "2027-06-30"); + assert!( + p.contains("temper.write(\"/artifact.md\""), + "author prompt must show the literal temper.write call" + ); + assert!( + p.contains("\"file_id\""), + "author prompt must name the file_id return field" + ); + assert!( + p.contains("Do NOT") && p.contains("Directories"), + "author prompt must forbid improvising Directories file creation" + ); + assert!( + !p.contains(""), + "no bare temper.write placeholder: the recipe captures result[\"file_id\"]" + ); + } + #[test] fn author_prompt_demands_both_kinds() { let p = author_prompt("w-1", "p-9", "file-log", "file-bundle", "2027-06-30"); diff --git a/os-apps/paw-foresight/wasm/sample_endpoints/src/lib.rs b/os-apps/paw-foresight/wasm/sample_endpoints/src/lib.rs index 8e12e3635..e65c5d12b 100644 --- a/os-apps/paw-foresight/wasm/sample_endpoints/src/lib.rs +++ b/os-apps/paw-foresight/wasm/sample_endpoints/src/lib.rs @@ -200,9 +200,15 @@ fn endpoint_writer_prompt( - at least one in-world primary document (a filing, a review, a changelog).\n\ Documents must contain specific dates, named actors, and numbers — vague futures \ cannot be repaired.\n\n\ - Save the whole bundle as ONE markdown file with temper.write, then self-report:\n\ + Save the whole bundle as ONE markdown file with the temper.write tool — this is the \ + ONLY way to create a FILE, and your workspace already exists. Do NOT create Files, \ + Directories, or Workspaces yourself, and do NOT invent a file-creation API via \ + temper.action — temper.write is the whole job. Call it exactly like this:\n\ + result = temper.write(\"/bundle.md\", \"\")\n\ + temper.write returns {{\"file_id\": \"...\", \"path\": \"...\", \"workspace_id\": \ + \"...\"}}. Use result[\"file_id\"] as bundle_file_id below, then self-report:\n\ temper.action(\"Endpoints\", \"{endpoint_id}\", \"BundleWritten\", \ - {{\"bundle_file_id\": \"\", \"summary\": \"\", \ + {{\"bundle_file_id\": \"\", \"summary\": \"\", \ \"author_agent_id\": \"{agent_id}\"}})\n\ Then call temper.done(\"complete\"). The diversity gate, not you, starts repair." ) @@ -1039,6 +1045,27 @@ mod tests { assert!(!p.contains("\"SubmitForRepair\"")); } + #[test] + fn writer_prompt_gives_explicit_file_write_recipe() { + let p = writer_prompt(&driver_stance(0, &[]), false); + assert!( + p.contains("temper.write(\"/bundle.md\""), + "writer prompt must show the literal temper.write call" + ); + assert!( + p.contains("\"file_id\""), + "writer prompt must name the file_id return field" + ); + assert!( + p.contains("Do NOT") && p.contains("Directories"), + "writer prompt must forbid improvising Directories file creation" + ); + assert!( + !p.contains(""), + "the bare placeholder must be gone: the recipe captures result[\"file_id\"]" + ); + } + #[test] fn re_steer_brief_demands_divergence_not_rewording() { let p = endpoint_writer_prompt( diff --git a/os-apps/paw-foresight/wasm/seed_world/src/lib.rs b/os-apps/paw-foresight/wasm/seed_world/src/lib.rs index e5a6089c3..b98193cac 100644 --- a/os-apps/paw-foresight/wasm/seed_world/src/lib.rs +++ b/os-apps/paw-foresight/wasm/seed_world/src/lib.rs @@ -119,10 +119,17 @@ fn surveyor_prompt( CONSENSUS POLE (what most informed observers currently expect). These are the \ dimensions the engine will sample diverse futures across, so make them orthogonal \ and genuinely contested — not restatements of one another.\n\n\ - Then write a one-page skeleton summary with temper.write (markdown), and finish by \ + Then write a one-page skeleton summary (markdown) with the temper.write tool — this \ + is the ONLY way to create a FILE, and your workspace already exists. Do NOT create \ + Files, Directories, or Workspaces yourself, and do NOT invent a file-creation API: \ + temper.create is for EventNodes only, never for files. Call temper.write exactly like \ + this:\n\ + result = temper.write(\"/skeleton.md\", \"\")\n\ + temper.write returns {{\"file_id\": \"...\", \"path\": \"...\", \"workspace_id\": \ + \"...\"}}. Use result[\"file_id\"] as graph_snapshot_file_id below, then finish by \ self-reporting (uncertainty_axes is a JSON array of {{\"axis\": \"...\", \"consensus_pole\": \"...\"}}):\n\ temper.action(\"Worlds\", \"{world_id}\", \"SeedComplete\", \ - {{\"skeleton_node_count\": \"\", \"graph_snapshot_file_id\": \"\", \ + {{\"skeleton_node_count\": \"\", \"graph_snapshot_file_id\": \"\", \ \"uncertainty_axes\": \"[{{\\\"axis\\\": \\\"...\\\", \\\"consensus_pole\\\": \\\"...\\\"}}, ...]\"}})\n\ Then call temper.done(\"complete\")." ) @@ -459,6 +466,36 @@ mod tests { ); } + #[test] + fn surveyor_prompt_gives_explicit_file_write_recipe() { + let p = surveyor_prompt( + "w-1", + "a-1", + "Test", + "ai coding tools", + "desc", + "2026-12-11", + "corpus", + false, + ); + assert!( + p.contains("temper.write(\"/skeleton.md\""), + "surveyor prompt must show the literal temper.write call" + ); + assert!( + p.contains("\"file_id\""), + "surveyor prompt must name the file_id return field" + ); + assert!( + p.contains("Do NOT") && p.contains("Directories"), + "surveyor prompt must forbid improvising Directories file creation" + ); + assert!( + !p.contains(""), + "the bare placeholder must be gone: the recipe captures result[\"file_id\"]" + ); + } + #[test] fn bookmaker_prompt_imports_market_provenance_only() { let p = bookmaker_prompt("w-1", "a-1", "ai coding tools", "2026-12-11", "", false); diff --git a/os-apps/paw-foresight/wasm/spawn_adversaries/src/lib.rs b/os-apps/paw-foresight/wasm/spawn_adversaries/src/lib.rs index 49526082d..2a23db339 100644 --- a/os-apps/paw-foresight/wasm/spawn_adversaries/src/lib.rs +++ b/os-apps/paw-foresight/wasm/spawn_adversaries/src/lib.rs @@ -100,10 +100,16 @@ fn adversary_prompt( \"contradiction|incentive|lag|miracle\", \"severity\": \"low|medium|high\", \"note\": \ \"...\"}}]. You refute; you never repair, and you never compute scores — do not add \ or fix EventNodes, and costing is deterministic and runs elsewhere.\n\n\ - Write a challenge log with temper.write (markdown: each attack with its reasoning), \ - then self-report:\n\ + Write your challenge log (markdown: each attack with its reasoning) with the \ + temper.write tool — this is the ONLY way to create a file, and your workspace already \ + exists. Do NOT create Files, Directories, or Workspaces yourself, and do NOT invent a \ + file-creation API via temper.action — temper.write is the whole job. Call it exactly \ + like this:\n\ + result = temper.write(\"/challenge-log.md\", \"\")\n\ + temper.write returns {{\"file_id\": \"...\", \"path\": \"...\", \"workspace_id\": \ + \"...\"}}. Use result[\"file_id\"] as challenge_log_file_id below, then self-report:\n\ temper.action(\"Paths\", \"{path_id}\", \"ChallengeComplete\", \ - {{\"challenge_log_file_id\": \"\", \"challenge_flags\": \ + {{\"challenge_log_file_id\": \"\", \"challenge_flags\": \ \"[{{\\\"kind\\\": \\\"...\\\", \\\"severity\\\": \\\"...\\\", \\\"note\\\": \ \\\"...\\\"}}]\"}})\n\ Then call temper.done(\"complete\")." @@ -461,6 +467,27 @@ mod tests { assert!(!p.contains("cost_flags")); } + #[test] + fn adversary_prompt_gives_explicit_file_write_recipe() { + let p = prompt(false); + assert!( + p.contains("temper.write(\"/challenge-log.md\""), + "adversary prompt must show the literal temper.write call" + ); + assert!( + p.contains("\"file_id\""), + "adversary prompt must name the file_id return field" + ); + assert!( + p.contains("Do NOT") && p.contains("Directories"), + "adversary prompt must forbid improvising Directories file creation" + ); + assert!( + !p.contains(""), + "the bare placeholder must be gone: the recipe captures result[\"file_id\"]" + ); + } + #[test] fn adversary_breaks_on_four_fronts_and_audits_honesty() { let p = prompt(false); diff --git a/os-apps/paw-foresight/wasm/spawn_repairers/src/lib.rs b/os-apps/paw-foresight/wasm/spawn_repairers/src/lib.rs index 636b8b387..f0829407f 100644 --- a/os-apps/paw-foresight/wasm/spawn_repairers/src/lib.rs +++ b/os-apps/paw-foresight/wasm/spawn_repairers/src/lib.rs @@ -232,10 +232,16 @@ fn repairer_prompt( - \"deformation\": you amended the claim to make it bridgeable\n\ Severity: \"low\" | \"medium\" | \"high\". You flag costs; you NEVER compute scores — \ costing is deterministic and runs elsewhere.\n\n\ - Write a repair log with temper.write (markdown: the backward chain with your \ - reasoning), then self-report:\n\ + Write your repair log (markdown: the backward chain with your reasoning) with the \ + temper.write tool — this is the ONLY way to create a FILE, and your workspace already \ + exists. Do NOT create Files, Directories, or Workspaces yourself, and do NOT invent a \ + file-creation API: temper.create is for EventNodes only, never for files. Call \ + temper.write exactly like this:\n\ + result = temper.write(\"/repair-log.md\", \"\")\n\ + temper.write returns {{\"file_id\": \"...\", \"path\": \"...\", \"workspace_id\": \ + \"...\"}}. Use result[\"file_id\"] as repair_log_file_id below, then self-report:\n\ temper.action(\"Paths\", \"{path_id}\", \"RepairComplete\", {{\"repair_log_file_id\": \ - \"\", \"required_node_ids\": \"[\\\"\\\", \ + \"\", \"required_node_ids\": \"[\\\"\\\", \ ...]\", \"cost_flags\": \"[{{\\\"kind\\\": \\\"...\\\", \\\"severity\\\": \\\"...\\\", \ \\\"note\\\": \\\"...\\\"}}]\"}})\n\ Then call temper.done(\"complete\")." @@ -775,6 +781,27 @@ mod tests { } } + #[test] + fn repairer_prompt_gives_explicit_file_write_recipe() { + let p = prompt(false); + assert!( + p.contains("temper.write(\"/repair-log.md\""), + "repairer prompt must show the literal temper.write call" + ); + assert!( + p.contains("\"file_id\""), + "repairer prompt must name the file_id return field" + ); + assert!( + p.contains("Do NOT") && p.contains("Directories"), + "repairer prompt must forbid improvising Directories file creation" + ); + assert!( + !p.contains(""), + "the bare placeholder must be gone: the recipe captures result[\"file_id\"]" + ); + } + #[test] fn repairer_works_backward_and_flags_all_five_kinds_without_scoring() { let p = prompt(false);