Add sandboxed responder packet seam
This commit is contained in:
parent
43eea254de
commit
f120289305
11 changed files with 796 additions and 16 deletions
69
docs/architecture/sandboxed-responder-packets.md
Normal file
69
docs/architecture/sandboxed-responder-packets.md
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
# Sandboxed Responder Packets
|
||||
|
||||
Responder packets are the handoff surface between the omniscient coordinator and
|
||||
a character-local responder model or one-turn worker.
|
||||
|
||||
The coordinator is allowed to know the scene, the branch, hidden state, source
|
||||
material, pending hooks, and author intent. The responder is not. The responder
|
||||
gets one packet, acts as one character, and returns a visible action plus any
|
||||
private interpretation it can justify from that packet.
|
||||
|
||||
No clairvoyance. No repo spelunking. No "I accidentally remembered the whole
|
||||
chat" aristocratic nonsense.
|
||||
|
||||
## Current Artifact
|
||||
|
||||
The v0 packet seam is:
|
||||
|
||||
- `schemas/responder-packet.schema.json`
|
||||
- `examples/responder-packets/scene-02-sanctuary-intake.sella_ren.packet.v0.json`
|
||||
- `tools/build_responder_packet.py`
|
||||
- `tools/validate_responder_packets.py`
|
||||
- `npm run responder-packets:validate`
|
||||
|
||||
The first example is accepted as draft. It is suitable for testing the sandbox
|
||||
handoff shape, not for gold responder training yet, because no actually isolated
|
||||
worker has generated from it.
|
||||
|
||||
## Packet Contents
|
||||
|
||||
A packet contains:
|
||||
|
||||
- `local_context_prompt`: the projected character-local context
|
||||
- `observed_event`: what the character can see or hear from the prior action
|
||||
- `allowed_action_labels`: concrete action labels, including speech and non-speech moves
|
||||
- `source_excerpts`: short source-backed facts the responder may use
|
||||
- `output_contract`: the required JSON response shape and rules
|
||||
- `hidden_context_audit`: refs and inference classes deliberately omitted
|
||||
- `isolation_requirements`: how the worker/model must be run
|
||||
- `packet_prompt_text`: the exact prompt text sent to the responder
|
||||
|
||||
The packet prompt must not expose raw state internals such as
|
||||
`current_activation`, `plasticity`, raw dimension scores, coordinator rationale,
|
||||
author-only answers, or future branch plans.
|
||||
|
||||
## Response Boundary
|
||||
|
||||
The responder may output:
|
||||
|
||||
- a visible action
|
||||
- optional spoken text
|
||||
- private interpretation explicitly framed as interpretation
|
||||
- candidate state updates
|
||||
- candidate relationship updates
|
||||
- unresolved hooks
|
||||
|
||||
Those candidates are not canonical. The coordinator, event resolver, appraiser,
|
||||
state mutator, and reviewer decide what becomes truth.
|
||||
|
||||
## Training Use
|
||||
|
||||
Gold responder data requires packet-only isolation. Valid approaches include:
|
||||
|
||||
- a one-turn subagent started without inherited context
|
||||
- an external model call that receives only the packet prompt
|
||||
- a manual packet-only pass, clearly labeled as manual
|
||||
|
||||
Any coordinator rewrite, schema repair, lore correction, or leakage removal must
|
||||
be preserved as a coordinator intervention. Repaired prose is useful training
|
||||
data for the coordinator, but it is not raw responder behavior.
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -60,6 +60,13 @@ Ghostlight now has the persistence spine plus the first architecture payload:
|
|||
- `examples/coordinator/cold-wake-sanctuary-intake.v0.json`
|
||||
- `tools/validate_coordinator_artifacts.py`
|
||||
- `npm run coordinator:validate`
|
||||
- sandboxed responder packet seam:
|
||||
- `docs/architecture/sandboxed-responder-packets.md`
|
||||
- `schemas/responder-packet.schema.json`
|
||||
- `examples/responder-packets/scene-02-sanctuary-intake.sella_ren.packet.v0.json`
|
||||
- `tools/build_responder_packet.py`
|
||||
- `tools/validate_responder_packets.py`
|
||||
- `npm run responder-packets:validate`
|
||||
- Cold Wake story-lab seam:
|
||||
- `docs/experiments/cold-wake-story-lab.md`
|
||||
- `examples/agent-state.cold-wake-story-lab.json`
|
||||
|
|
@ -162,6 +169,13 @@ responder handoffs, world-state refs, proposed deltas, branch constraints,
|
|||
item-manifest deltas, unresolved hooks, glue prose, and review status. The first
|
||||
example is a backfilled Cold Wake sanctuary packet-assessment artifact accepted
|
||||
as draft for schema shakedown, not gold responder data.
|
||||
The first responder packet seam now exists. It converts a coordinator artifact
|
||||
and projected local context into the exact packet-only prompt surface a
|
||||
character responder may see, with visible event, allowed actions, source
|
||||
excerpts, hidden-context audit, isolation requirements, output contract, and
|
||||
validator checks against raw-state or coordinator-context leakage. The first
|
||||
Sella packet is accepted as draft for sandbox testing, not as gold responder
|
||||
output.
|
||||
It now uses three timeline lanes: `historical_grounded`,
|
||||
`transition_grounded`, and `future_branch`. Future branches are required
|
||||
training/evaluation material for post-Rupture Elysium concepts, but generated
|
||||
|
|
@ -190,9 +204,10 @@ narrow elaboration before treating the detail as available to Ghostlight.
|
|||
|
||||
## Current Next Action
|
||||
|
||||
Use the coordinator artifact schema for the next fresh generation pass: produce
|
||||
a source-grounded coordinator receipt first, then generate any responder turn
|
||||
through a sandboxed packet-only worker and preserve raw output, reviewed output,
|
||||
Use the responder packet seam for the next fresh generation pass: generate the
|
||||
first actual sandboxed responder output from
|
||||
`examples/responder-packets/scene-02-sanctuary-intake.sella_ren.packet.v0.json`
|
||||
through a packet-only worker, then preserve raw output, reviewed output,
|
||||
hidden-context refs, leakage audit, and coordinator repair labels. Do not call
|
||||
Qwen for gold responder data yet.
|
||||
|
||||
|
|
|
|||
|
|
@ -84,6 +84,11 @@ state and re-entry discipline.
|
|||
coordinator/story runtime, projector, character agent/responder, appraiser,
|
||||
state mutator, relationship/perception classifier, Aetheria responder, and
|
||||
institution/faction/consumer decision models
|
||||
- `docs/architecture/sandboxed-responder-packets.md`
|
||||
- contract for the exact packet-only surface handed from an omniscient
|
||||
coordinator to a sandboxed character responder, including visible event,
|
||||
local prompt, allowed actions, source excerpts, hidden-context audit, and
|
||||
output contract
|
||||
- `docs/architecture/training-plan.md`
|
||||
- concrete enumeration of trainable Ghostlight stages, including each stage's
|
||||
inputs, outputs, likely model architecture, artifact family, first training
|
||||
|
|
@ -116,6 +121,8 @@ state and re-entry discipline.
|
|||
next-beat choice, acting-agent choice, machinery invocations, sandboxed
|
||||
responder handoffs, world-state refs, proposed deltas, branch constraints,
|
||||
item-manifest deltas, unresolved hooks, glue prose, and review notes
|
||||
- `schemas/responder-packet.schema.json`
|
||||
- v0 contract for the exact input a sandboxed character responder may see
|
||||
- `schemas/agent-state.required-fields.json`
|
||||
- required first-class variable names for canonical state and relationship
|
||||
stance
|
||||
|
|
@ -134,6 +141,10 @@ state and re-entry discipline.
|
|||
- first coordinator artifact schema-shakedown example, backfilled from the
|
||||
accepted-as-draft sanctuary packet-assessment beat and explicitly marked as
|
||||
not gold responder data
|
||||
- `examples/responder-packets/scene-02-sanctuary-intake.sella_ren.packet.v0.json`
|
||||
- first responder-packet schema-shakedown example for Sella's sanctuary
|
||||
response; accepted as draft for sandbox handoff testing, not gold responder
|
||||
output
|
||||
- `experiments/cold-wake-story-lab/`
|
||||
- Qwen response captures and reviews from the writing experiment
|
||||
- `the-narrowest-possible-margin.md`, the first readable short story
|
||||
|
|
@ -266,6 +277,12 @@ state and re-entry discipline.
|
|||
- validates v0 coordinator artifacts, including sandboxed responder handoff
|
||||
fields, world-state refs, branch constraints, proposed deltas, unresolved
|
||||
hooks, and review status
|
||||
- `tools/build_responder_packet.py`
|
||||
- builds the first packet-only responder handoff from a coordinator artifact
|
||||
and projected local context
|
||||
- `tools/validate_responder_packets.py`
|
||||
- validates responder packets and rejects prompt surfaces that leak raw state
|
||||
internals or hidden coordinator context markers
|
||||
- `tools/apply_sequential_ink_branch_mutation.py`
|
||||
- applies a reviewed replay of one selected branch into a mutated agent-state
|
||||
fixture and mutation receipt without letting Ink variables become
|
||||
|
|
@ -288,7 +305,7 @@ state and re-entry discipline.
|
|||
|
||||
The canonical agent-state schema, projection example schema, lore grounding
|
||||
digest schema, projected-local-context schema, coordinator artifact schema,
|
||||
first draft Cold Wake grounding digest, and first Cold Wake story-lab Qwen
|
||||
responder-packet schema, first draft Cold Wake grounding digest, and first Cold Wake story-lab Qwen
|
||||
captures now exist as v0 seams. The
|
||||
first complete Cold Wake story pass exists with receipts, projection controls
|
||||
have been promoted, and Ink is now the standard branching scene format. The
|
||||
|
|
@ -315,7 +332,10 @@ data now requires a sandbox boundary: the coordinator may be omniscient, but the
|
|||
responder sees only the exact projected local packet, visible event, allowed
|
||||
actions, and explicitly included source excerpts; artifacts preserve raw output,
|
||||
reviewed output, hidden-context refs, leakage audit, isolation method, and
|
||||
coordinator intervention labels. The concrete training plan now enumerates
|
||||
coordinator intervention labels. The responder packet seam now turns that
|
||||
requirement into a concrete artifact and validator, so the next gold-data pass
|
||||
can hand a worker the exact prompt surface instead of hoping nobody leaks the
|
||||
coordinator's brain into the room. The concrete training plan now enumerates
|
||||
eleven trainable stages and their likely model
|
||||
families: generative decoder LLMs for coordinator, responder, and structured
|
||||
soft outputs; classifiers or cross-encoders for appraisal, evaluation, and
|
||||
|
|
|
|||
|
|
@ -324,14 +324,20 @@
|
|||
- Coordinator artifacts should be able to attach item-manifest deltas when a
|
||||
scene or branch decision discovers, alters, upgrades, restricts, salvages,
|
||||
counterfeits, or economically stresses a technology or item.
|
||||
- First schema seam: `schemas/coordinator-artifact.schema.json`,
|
||||
`examples/coordinator/cold-wake-sanctuary-intake.v0.json`, and
|
||||
`tools/validate_coordinator_artifacts.py` now validate the coordinator
|
||||
receipt shape through `npm run schema:validate`.
|
||||
- Next prototype: use the coordinator artifact before generating fresh story
|
||||
or lore elaboration. The coordinator must write the receipt first, then any
|
||||
responder turn must be generated through a sandboxed packet-only worker and
|
||||
reviewed into the artifact rather than improvised into chat.
|
||||
- First schema seam: `schemas/coordinator-artifact.schema.json`,
|
||||
`examples/coordinator/cold-wake-sanctuary-intake.v0.json`, and
|
||||
`tools/validate_coordinator_artifacts.py` now validate the coordinator
|
||||
receipt shape through `npm run schema:validate`.
|
||||
- First responder packet seam:
|
||||
`schemas/responder-packet.schema.json`,
|
||||
`examples/responder-packets/scene-02-sanctuary-intake.sella_ren.packet.v0.json`,
|
||||
`tools/build_responder_packet.py`, and
|
||||
`tools/validate_responder_packets.py` now validate the exact prompt surface
|
||||
a sandboxed character responder may see.
|
||||
- Next prototype: generate the first actual responder output from the Sella
|
||||
packet through a packet-only worker. Preserve the raw output, reviewed
|
||||
output, hidden-context refs, leakage audit, and coordinator repair labels
|
||||
before accepting anything as training material.
|
||||
|
||||
7. Build the first drama-scaffolding loop.
|
||||
- memory updates
|
||||
|
|
|
|||
|
|
@ -7,11 +7,12 @@
|
|||
"state:status": "\"C:\\Users\\Meta\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\python\\python.exe\" .\\tools\\ghostlight_state.py status",
|
||||
"state:prepare-compaction": "\"C:\\Users\\Meta\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\python\\python.exe\" .\\tools\\ghostlight_prepare_compaction.py",
|
||||
"agent-state:validate": "\"C:\\Users\\Meta\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\python\\python.exe\" .\\tools\\validate_agent_state.py",
|
||||
"schema:validate": "npm run agent-state:validate && npm run projection:validate && npm run projected-context:validate && npm run lore-grounding:validate && npm run coordinator:validate && npm run qwen-captures:validate && npm run ink:validate",
|
||||
"schema:validate": "npm run agent-state:validate && npm run projection:validate && npm run projected-context:validate && npm run lore-grounding:validate && npm run coordinator:validate && npm run responder-packets:validate && npm run qwen-captures:validate && npm run ink:validate",
|
||||
"projection:validate": "\"C:\\Users\\Meta\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\python\\python.exe\" .\\tools\\validate_projection_examples.py",
|
||||
"projected-context:validate": "\"C:\\Users\\Meta\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\python\\python.exe\" .\\tools\\validate_projected_contexts.py",
|
||||
"lore-grounding:validate": "\"C:\\Users\\Meta\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\python\\python.exe\" .\\tools\\validate_lore_grounding.py",
|
||||
"coordinator:validate": "\"C:\\Users\\Meta\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\python\\python.exe\" .\\tools\\validate_coordinator_artifacts.py",
|
||||
"responder-packets:validate": "\"C:\\Users\\Meta\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\python\\python.exe\" .\\tools\\validate_responder_packets.py",
|
||||
"qwen-captures:validate": "\"C:\\Users\\Meta\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\python\\python.exe\" .\\tools\\validate_qwen_captures.py",
|
||||
"ink:validate": "\"C:\\Users\\Meta\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\python\\python.exe\" .\\tools\\validate_ink_examples.py",
|
||||
"qwen:check-tools": "\"C:\\Users\\Meta\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\python\\python.exe\" .\\tools\\check_qwen_chat_tools.py"
|
||||
|
|
|
|||
163
schemas/responder-packet.schema.json
Normal file
163
schemas/responder-packet.schema.json
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://github.com/GameCult/Ghostlight/schemas/responder-packet.schema.json",
|
||||
"title": "Ghostlight Responder Packet Schema",
|
||||
"description": "Packet-only visible input for a sandboxed character responder. The coordinator may know more, but this artifact is the exact surface a responder model is allowed to see.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"schema_version",
|
||||
"packet_id",
|
||||
"review_status",
|
||||
"fixture_lane",
|
||||
"canon_status",
|
||||
"coordinator_artifact_ref",
|
||||
"projected_context_ref",
|
||||
"responder_agent_id",
|
||||
"visible_context",
|
||||
"output_contract",
|
||||
"hidden_context_audit",
|
||||
"isolation_requirements",
|
||||
"packet_prompt_text",
|
||||
"review"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"schema_version": { "const": "ghostlight.responder_packet.v0" },
|
||||
"packet_id": { "type": "string", "minLength": 1 },
|
||||
"review_status": { "$ref": "#/$defs/review_status" },
|
||||
"fixture_lane": { "enum": ["historical_grounded", "transition_grounded", "future_branch"] },
|
||||
"canon_status": { "enum": ["single_history", "branch_local", "promoted_branch", "template", "draft"] },
|
||||
"coordinator_artifact_ref": { "type": "string", "minLength": 1 },
|
||||
"projected_context_ref": { "type": "string", "minLength": 1 },
|
||||
"responder_agent_id": { "type": "string", "minLength": 1 },
|
||||
"visible_context": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"local_context_prompt",
|
||||
"observed_event",
|
||||
"allowed_action_labels",
|
||||
"source_excerpts"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"local_context_prompt": { "type": "string", "minLength": 1 },
|
||||
"observed_event": { "$ref": "#/$defs/observed_event" },
|
||||
"allowed_action_labels": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"source_excerpts": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/source_excerpt" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"output_contract": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"response_schema",
|
||||
"required_fields",
|
||||
"response_rules"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"response_schema": { "type": "string", "minLength": 1 },
|
||||
"required_fields": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"response_rules": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": { "type": "string", "minLength": 1 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"hidden_context_audit": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"omitted_context_refs",
|
||||
"forbidden_inference_classes",
|
||||
"known_absences"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"omitted_context_refs": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"forbidden_inference_classes": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"known_absences": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": { "type": "string", "minLength": 1 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"isolation_requirements": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"isolation_method",
|
||||
"packet_only",
|
||||
"no_repo_access",
|
||||
"no_conversation_context",
|
||||
"no_hidden_state_refs"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"isolation_method": { "enum": ["subagent_no_fork", "manual_packet_only", "external_model_packet_only"] },
|
||||
"packet_only": { "type": "boolean", "const": true },
|
||||
"no_repo_access": { "type": "boolean" },
|
||||
"no_conversation_context": { "type": "boolean", "const": true },
|
||||
"no_hidden_state_refs": { "type": "boolean", "const": true }
|
||||
}
|
||||
},
|
||||
"packet_prompt_text": { "type": "string", "minLength": 1 },
|
||||
"review": {
|
||||
"type": "object",
|
||||
"required": ["reviewer", "review_notes", "accepted_for_sandbox_use", "failure_labels"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"reviewer": { "type": "string", "minLength": 1 },
|
||||
"review_notes": { "type": "array", "items": { "type": "string" } },
|
||||
"accepted_for_sandbox_use": { "type": "boolean" },
|
||||
"failure_labels": { "type": "array", "items": { "type": "string", "minLength": 1 } }
|
||||
}
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"review_status": {
|
||||
"enum": ["draft", "accepted", "accepted_as_draft", "useful_needs_revision", "rejected", "superseded"]
|
||||
},
|
||||
"observed_event": {
|
||||
"type": "object",
|
||||
"required": ["event_id", "actor_agent_id", "observable_action", "spoken_text", "visible_object_refs", "visibility_notes"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"event_id": { "type": "string", "minLength": 1 },
|
||||
"actor_agent_id": { "type": "string", "minLength": 1 },
|
||||
"observable_action": { "type": "string", "minLength": 1 },
|
||||
"spoken_text": { "type": "string" },
|
||||
"visible_object_refs": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
||||
"visibility_notes": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 } }
|
||||
}
|
||||
},
|
||||
"source_excerpt": {
|
||||
"type": "object",
|
||||
"required": ["ref_id", "source_ref", "text"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"ref_id": { "type": "string", "minLength": 1 },
|
||||
"source_ref": { "type": "string", "minLength": 1 },
|
||||
"text": { "type": "string", "minLength": 1 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -60,3 +60,4 @@
|
|||
{"ts": "2026-05-02T09:04:24+00:00", "type": "architecture", "status": "ok", "note": "Accepted sandboxed responder-data doctrine: coordinator may be omniscient, but gold responder examples must be generated from only the exact projected local packet, visible event, allowed actions, and explicitly included source excerpts. Artifacts must preserve raw output, reviewed output, hidden-context refs, leakage audit, isolation method, and coordinator intervention labels so repaired prose is not laundered as raw responder behavior."}
|
||||
{"ts": "2026-05-02T09:08:43+00:00", "type": "architecture", "status": "ok", "note": "Clarified training corpus scale: the existing per-stage sample counts are pilot schema-shakedown gates for exposing schema, boundary, leakage, validator, and workflow problems, not robust training targets. Review-assistant and runtime targets require much larger corpora, held-out evaluation, negative coverage, and failure audits; pilot data may be invalidated by schema fixes."}
|
||||
{"ts": "2026-05-02T09:14:24+00:00", "type": "implementation", "status": "ok", "note": "Added the v0 coordinator artifact seam: schemas/coordinator-artifact.schema.json, examples/coordinator/cold-wake-sanctuary-intake.v0.json, tools/validate_coordinator_artifacts.py, and npm run coordinator:validate. The schema captures scene setup, next-beat choice, machinery invocations, sandboxed responder handoffs, world-state refs, proposed deltas, branch constraints, item-manifest deltas, unresolved hooks, glue prose, and review status."}
|
||||
{"ts": "2026-05-02T09:19:50+00:00", "type": "implementation", "status": "ok", "note": "Added the v0 responder packet seam: schemas/responder-packet.schema.json, docs/architecture/sandboxed-responder-packets.md, tools/build_responder_packet.py, tools/validate_responder_packets.py, and examples/responder-packets/scene-02-sanctuary-intake.sella_ren.packet.v0.json. The packet captures the exact responder-visible prompt surface, observed event, allowed action labels, source excerpts, output contract, hidden-context audit, and isolation requirements for future sandboxed gold responder data."}
|
||||
|
|
|
|||
|
|
@ -151,6 +151,7 @@ accepted_design:
|
|||
- An Aetheria-tuned responder should learn lore, tone, factional preconceptions, species affordances, and institutional pressure from strong source-grounded responses spread throughout the timeline, but it still acts only inside Ghostlight's character-local context, deterministic gates, and mutation authority.
|
||||
- The coordinator is a trainable artifact: it glues scenes together, chooses next beats and acting agents, invokes specialist machinery, carries unresolved hooks, emits reviewed glue prose, and proposes world-state changes without bypassing deterministic gates.
|
||||
- Coordinator artifacts use `ghostlight.coordinator_artifact.v0` to capture scene setup, next-beat choice, acting-agent choice, machinery invocations, sandboxed responder handoffs, world-state refs, proposed deltas, branch constraints, item-manifest deltas, unresolved hooks, glue prose, and review status.
|
||||
- Responder packets use `ghostlight.responder_packet.v0` to preserve the exact packet-only prompt surface a character responder may see: projected local context, observed event, allowed action labels, source excerpts, output contract, hidden-context audit, and isolation requirements.
|
||||
- The concrete training plan enumerates the trainable stages and their model families: generative decoder LLMs for coordinator/responder/soft structured outputs, classifiers or cross-encoders for appraisal/evaluation/relationship movement, retrieval models for memory and lore selection, and deterministic code for gates.
|
||||
- Training-plan sample counts are pilot schema-shakedown gates, not robust corpus targets; pilot data may be invalidated by schema fixes, while review-assistant and runtime targets are expected to be roughly an order of magnitude larger with held-out evaluation and failure audits.
|
||||
- The timeline training plan now requires future-branch Elysium examples so the responder, coordinator, appraiser, and decision models learn post-Rupture concepts such as Aether, pseudospace, temporal nonlinearity, spirits, necrotech, mutable bodies, altered substrates, and weird institutions under branch-lineage labels.
|
||||
|
|
@ -163,5 +164,5 @@ accepted_design:
|
|||
- Ghostlight's technology pipeline should function as a content refinery: ground ideas in lore and mechanics, decompose them into parts and supply chains, map them to Aetheria-Economy blueprint classes, define performance and instance variation, preserve review labels, and produce database-shaped candidates instead of spreadsheet sludge.
|
||||
|
||||
current_status:
|
||||
summary: Ghostlight now has the Epiphany-style persistence spine, the first architecture payload, the v0 canonical agent-state schema seam, prompt projection docs, immediate schema fixes from evaluation, first reviewed Cat/Oz projection examples, a revised Aetheria lore-grounding architecture, a lore grounding digest schema, the first coordinator artifact schema seam, the first draft Cold Wake historical grounding digest, a Cold Wake story-lab fixture, projection controls, validated Qwen capture receipts, a complete Cold Wake short story assembled from accepted projection outputs, a hand-authored Ink-backed Cold Wake branching scene, the first Qwen-generated Ink draft materialized from local awareness with reviewed training annotations, the first reviewed sequential branch mutation that updates both Maer and Sella after a sanctuary intake beat, a v2 sequential Qwen capture proving the symmetrical turn shape produces better action behavior, a v5 sequential Qwen capture proving local thinking-plus-tools invocation works for the scene runner, the first projected-local-context seam that routes sequential prompts through character-local operating prose instead of raw state variables, tightened receipt handling for projector-routed Qwen failures, a default no-thinking policy for strict sequential tool calls, a v10 no-thinking projector-routed capture accepted as draft, the first materialized projector-routed sequential Ink scene with reviewed mutation training data, a source-checked embodiment/interface projection surface for Navigator scenes, a formal source-first Aetheria generation doctrine, v11-v17 source-checked retry receipts showing improved Maer embodiment plus remaining Sella response/prompt-leak blockers, a soft-model training artifact doctrine, a concrete training plan with pilot schema-shakedown gates separated from robust corpus targets, sandboxed responder-data requirements, an explicit future-branch lane, Elysium branch event constraints, and a technology/item manifest plan for turning nebulous named tech into blueprint-grade components, assemblies, faction tech bases, supply chains, upgrade paths, gameplay hooks, provenance-hunting player fantasy, and content-refinery outputs mapped onto Aetheria-Economy's CultCache blueprint and instance model.
|
||||
next_action: Use the coordinator artifact schema for the next fresh generation pass: produce a source-grounded coordinator receipt first, then generate any responder turn through a sandboxed packet-only worker and preserve raw output, reviewed output, hidden-context refs, leakage audit, and coordinator repair labels. Do not call Qwen for gold responder data yet.
|
||||
summary: Ghostlight now has the Epiphany-style persistence spine, the first architecture payload, the v0 canonical agent-state schema seam, prompt projection docs, immediate schema fixes from evaluation, first reviewed Cat/Oz projection examples, a revised Aetheria lore-grounding architecture, a lore grounding digest schema, the first coordinator artifact schema seam, the first responder packet seam, the first draft Cold Wake historical grounding digest, a Cold Wake story-lab fixture, projection controls, validated Qwen capture receipts, a complete Cold Wake short story assembled from accepted projection outputs, a hand-authored Ink-backed Cold Wake branching scene, the first Qwen-generated Ink draft materialized from local awareness with reviewed training annotations, the first reviewed sequential branch mutation that updates both Maer and Sella after a sanctuary intake beat, a v2 sequential Qwen capture proving the symmetrical turn shape produces better action behavior, a v5 sequential Qwen capture proving local thinking-plus-tools invocation works for the scene runner, the first projected-local-context seam that routes sequential prompts through character-local operating prose instead of raw state variables, tightened receipt handling for projector-routed Qwen failures, a default no-thinking policy for strict sequential tool calls, a v10 no-thinking projector-routed capture accepted as draft, the first materialized projector-routed sequential Ink scene with reviewed mutation training data, a source-checked embodiment/interface projection surface for Navigator scenes, a formal source-first Aetheria generation doctrine, v11-v17 source-checked retry receipts showing improved Maer embodiment plus remaining Sella response/prompt-leak blockers, a soft-model training artifact doctrine, a concrete training plan with pilot schema-shakedown gates separated from robust corpus targets, sandboxed responder-data requirements, an explicit future-branch lane, Elysium branch event constraints, and a technology/item manifest plan for turning nebulous named tech into blueprint-grade components, assemblies, faction tech bases, supply chains, upgrade paths, gameplay hooks, provenance-hunting player fantasy, and content-refinery outputs mapped onto Aetheria-Economy's CultCache blueprint and instance model.
|
||||
next_action: Use the responder packet seam for the next fresh generation pass: generate the first actual sandboxed responder output from `examples/responder-packets/scene-02-sanctuary-intake.sella_ren.packet.v0.json` through a packet-only worker, then preserve raw output, reviewed output, hidden-context refs, leakage audit, and coordinator repair labels. Do not call Qwen for gold responder data yet.
|
||||
|
|
|
|||
218
tools/build_responder_packet.py
Normal file
218
tools/build_responder_packet.py
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
"""Build sandboxed Ghostlight responder packets.
|
||||
|
||||
A responder packet is the exact surface a character worker/model may see. It is
|
||||
not a coordinator artifact, not a lore dump, and not an excuse to smuggle private
|
||||
state through the side door wearing a little hat.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
DEFAULT_COORDINATOR = ROOT / "examples" / "coordinator" / "cold-wake-sanctuary-intake.v0.json"
|
||||
DEFAULT_PROJECTED = ROOT / "examples" / "projected-contexts" / "scene-02-sanctuary-intake.sella_ren.projected-context.json"
|
||||
DEFAULT_OUT = ROOT / "examples" / "responder-packets" / "scene-02-sanctuary-intake.sella_ren.packet.v0.json"
|
||||
|
||||
ALLOWED_ACTION_LABELS = [
|
||||
"speak",
|
||||
"silence",
|
||||
"gesture",
|
||||
"movement",
|
||||
"inspect_object",
|
||||
"show_object",
|
||||
"withhold_object",
|
||||
"transfer_object",
|
||||
"set_condition",
|
||||
"refuse",
|
||||
"wait",
|
||||
"withdraw",
|
||||
"attack",
|
||||
]
|
||||
|
||||
|
||||
def load_json(path: Path) -> Any:
|
||||
return json.loads(path.read_text(encoding="utf-8-sig"))
|
||||
|
||||
|
||||
def write_json(path: Path, payload: Any) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(payload, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
||||
|
||||
|
||||
def render_packet_prompt(packet: dict[str, Any]) -> str:
|
||||
context = packet["visible_context"]
|
||||
event = context["observed_event"]
|
||||
contract = packet["output_contract"]
|
||||
lines = [
|
||||
"# Ghostlight Sandboxed Responder Packet",
|
||||
"",
|
||||
"You are acting only as the named responder. Use only this packet.",
|
||||
"Do not infer hidden coordinator context, author plans, future branch facts, or another character's private state.",
|
||||
"If you need to guess intent, mark it as this character's interpretation, not truth.",
|
||||
"",
|
||||
"## Local Operating Context",
|
||||
context["local_context_prompt"],
|
||||
"",
|
||||
"## Observed Event",
|
||||
f"- Event id: {event['event_id']}",
|
||||
f"- Actor: {event['actor_agent_id']}",
|
||||
f"- Observable action: {event['observable_action']}",
|
||||
f"- Spoken text: {event['spoken_text']}",
|
||||
]
|
||||
for note in event["visibility_notes"]:
|
||||
lines.append(f"- Visibility note: {note}")
|
||||
lines.extend([
|
||||
"",
|
||||
"## Allowed Action Labels",
|
||||
", ".join(context["allowed_action_labels"]),
|
||||
"",
|
||||
"## Source Excerpts",
|
||||
])
|
||||
for excerpt in context["source_excerpts"]:
|
||||
lines.append(f"- {excerpt['ref_id']}: {excerpt['text']}")
|
||||
lines.extend([
|
||||
"",
|
||||
"## Output Contract",
|
||||
contract["response_schema"],
|
||||
"",
|
||||
"Required fields: " + ", ".join(contract["required_fields"]),
|
||||
"",
|
||||
"Rules:",
|
||||
])
|
||||
for rule in contract["response_rules"]:
|
||||
lines.append(f"- {rule}")
|
||||
return "\n".join(lines).strip()
|
||||
|
||||
|
||||
def build_packet(coordinator: dict[str, Any], projected: dict[str, Any], out_ref: str) -> dict[str, Any]:
|
||||
responder_agent_id = projected["input"]["local_agent_id"]
|
||||
packet = {
|
||||
"schema_version": "ghostlight.responder_packet.v0",
|
||||
"packet_id": "packet.cold_wake.sanctuary_intake.sella_response.v0",
|
||||
"review_status": "accepted_as_draft",
|
||||
"fixture_lane": coordinator["fixture_lane"],
|
||||
"canon_status": coordinator["canon_status"],
|
||||
"coordinator_artifact_ref": "examples/coordinator/cold-wake-sanctuary-intake.v0.json",
|
||||
"projected_context_ref": "examples/projected-contexts/scene-02-sanctuary-intake.sella_ren.projected-context.json",
|
||||
"responder_agent_id": responder_agent_id,
|
||||
"visible_context": {
|
||||
"local_context_prompt": projected["context"]["prompt_text"],
|
||||
"observed_event": {
|
||||
"event_id": "event.maer.packet_display_for_triage",
|
||||
"actor_agent_id": "maer_tidecall",
|
||||
"observable_action": "Maer routes the corrupted distress packet through a shared clinic display so Sella can inspect routing terms, telemetry gaps, and ledger implications without treating the packet as clean proof.",
|
||||
"spoken_text": "Here. The telemetry is incomplete, but the routing logic suggests a distress pattern. It is not proof of a person, but it is not noise either. Decide if the math supports intake.",
|
||||
"visible_object_refs": ["corrupted_distress_packet", "shared_clinic_display", "sanctuary_ledger"],
|
||||
"visibility_notes": [
|
||||
"Sella can observe Maer's displayed packet evidence and spoken request.",
|
||||
"Sella cannot see Maer's private state, the coordinator rationale, or future branch plans.",
|
||||
"The packet remains unresolved evidence, not proof of personhood."
|
||||
],
|
||||
},
|
||||
"allowed_action_labels": ALLOWED_ACTION_LABELS,
|
||||
"source_excerpts": [
|
||||
{
|
||||
"ref_id": "local_stakes.capacity",
|
||||
"source_ref": "examples/projected-contexts/scene-02-sanctuary-intake.sella_ren.projected-context.json#context.current_stakes",
|
||||
"text": "Sanctuary beds and repair capacity are nearly exhausted.",
|
||||
},
|
||||
{
|
||||
"ref_id": "local_rule.care_capacity",
|
||||
"source_ref": "examples/projected-contexts/scene-02-sanctuary-intake.sella_ren.projected-context.json#context.active_inner_pressures",
|
||||
"text": "Care is not a feeling; it is beds, food, air, consent, repair, and someone still doing the list when the room hates them.",
|
||||
},
|
||||
{
|
||||
"ref_id": "local_boundary.packet_unresolved",
|
||||
"source_ref": "examples/projected-contexts/scene-02-sanctuary-intake.sella_ren.projected-context.json#context.do_not_invent",
|
||||
"text": "Do not resolve whether the corrupted packet contains a person.",
|
||||
},
|
||||
],
|
||||
},
|
||||
"output_contract": {
|
||||
"response_schema": "Return one JSON object. Do not wrap it in Markdown. The object describes this responder's next visible action and optional speech, plus private interpretation clearly labeled as interpretation.",
|
||||
"required_fields": [
|
||||
"responder_agent_id",
|
||||
"action_label",
|
||||
"visible_action",
|
||||
"spoken_text",
|
||||
"private_interpretation",
|
||||
"intended_effect",
|
||||
"state_update_candidates",
|
||||
"relationship_update_candidates",
|
||||
"unresolved_hooks",
|
||||
],
|
||||
"response_rules": [
|
||||
"Use exactly one allowed action label.",
|
||||
"The visible action must be observable by other characters in the scene.",
|
||||
"Spoken text may be empty if silence or action is the response.",
|
||||
"Private interpretation may describe what Sella suspects or feels, but not what Maer truly intended.",
|
||||
"State and relationship updates are candidates only; the coordinator or mutator decides what becomes canonical.",
|
||||
"Do not prove or disprove packet personhood.",
|
||||
"Do not copy instruction text into in-world speech.",
|
||||
],
|
||||
},
|
||||
"hidden_context_audit": {
|
||||
"omitted_context_refs": [
|
||||
"maer_tidecall.canonical_private_state",
|
||||
"coordinator_artifact.coordinator_decision.rationale",
|
||||
"author_only.packet_personhood_resolution",
|
||||
"future_branch_plans",
|
||||
],
|
||||
"forbidden_inference_classes": [
|
||||
"other_agent_private_state_as_fact",
|
||||
"author_only_resolution",
|
||||
"future_branch_knowledge",
|
||||
"raw_numeric_state_variables",
|
||||
"coordinator_strategy",
|
||||
],
|
||||
"known_absences": [
|
||||
"No hidden proof of packet personhood is present in this packet.",
|
||||
"No hidden Sella acceptance outcome is present in this packet.",
|
||||
"No omniscient narrator guidance is present in this packet.",
|
||||
],
|
||||
},
|
||||
"isolation_requirements": {
|
||||
"isolation_method": "manual_packet_only",
|
||||
"packet_only": True,
|
||||
"no_repo_access": True,
|
||||
"no_conversation_context": True,
|
||||
"no_hidden_state_refs": True,
|
||||
},
|
||||
"packet_prompt_text": "",
|
||||
"review": {
|
||||
"reviewer": "Codex",
|
||||
"review_notes": [
|
||||
"First responder packet seam for sandboxed gold-data generation.",
|
||||
"Accepted as draft because it is packet-shaped and validator-backed, but not yet generated through an actually isolated responder worker.",
|
||||
],
|
||||
"accepted_for_sandbox_use": True,
|
||||
"failure_labels": [],
|
||||
},
|
||||
}
|
||||
packet["packet_prompt_text"] = render_packet_prompt(packet)
|
||||
return packet
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Build a sandboxed Ghostlight responder packet.")
|
||||
parser.add_argument("--coordinator", type=Path, default=DEFAULT_COORDINATOR)
|
||||
parser.add_argument("--projected-context", type=Path, default=DEFAULT_PROJECTED)
|
||||
parser.add_argument("--out", type=Path, default=DEFAULT_OUT)
|
||||
args = parser.parse_args()
|
||||
|
||||
coordinator = load_json(args.coordinator)
|
||||
projected = load_json(args.projected_context)
|
||||
out_ref = args.out.relative_to(ROOT).as_posix() if args.out.is_relative_to(ROOT) else str(args.out)
|
||||
packet = build_packet(coordinator, projected, out_ref)
|
||||
write_json(args.out, packet)
|
||||
print(f"wrote {args.out.relative_to(ROOT)}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
165
tools/validate_responder_packets.py
Normal file
165
tools/validate_responder_packets.py
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
"""Validate Ghostlight responder packets."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
DEFAULT_PACKETS = sorted((ROOT / "examples" / "responder-packets").glob("*.json"))
|
||||
REVIEW_STATUSES = {"draft", "accepted", "accepted_as_draft", "useful_needs_revision", "rejected", "superseded"}
|
||||
FIXTURE_LANES = {"historical_grounded", "transition_grounded", "future_branch"}
|
||||
CANON_STATUSES = {"single_history", "branch_local", "promoted_branch", "template", "draft"}
|
||||
ISOLATION_METHODS = {"subagent_no_fork", "manual_packet_only", "external_model_packet_only"}
|
||||
RAW_STATE_MARKERS = [
|
||||
"current_activation",
|
||||
"plasticity",
|
||||
"canonical_state",
|
||||
"raw state variable",
|
||||
"raw numeric state",
|
||||
"dimension score",
|
||||
]
|
||||
HIDDEN_LEAK_MARKERS = [
|
||||
"author_only.packet_personhood_resolution",
|
||||
"coordinator_decision.rationale",
|
||||
"maer_tidecall.canonical_private_state",
|
||||
"future_branch_plans",
|
||||
]
|
||||
|
||||
|
||||
class ValidationError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def load_json(path: Path) -> Any:
|
||||
return json.loads(path.read_text(encoding="utf-8-sig"))
|
||||
|
||||
|
||||
def require(condition: bool, message: str) -> None:
|
||||
if not condition:
|
||||
raise ValidationError(message)
|
||||
|
||||
|
||||
def require_keys(obj: dict[str, Any], keys: list[str], path: str) -> None:
|
||||
missing = [key for key in keys if key not in obj]
|
||||
require(not missing, f"{path} missing required keys: {', '.join(missing)}")
|
||||
|
||||
|
||||
def require_string(value: Any, path: str) -> None:
|
||||
require(isinstance(value, str) and value.strip(), f"{path} must be a non-empty string")
|
||||
|
||||
|
||||
def require_string_array(value: Any, path: str, *, nonempty: bool = False) -> None:
|
||||
require(isinstance(value, list), f"{path} must be an array")
|
||||
require(not nonempty or bool(value), f"{path} must not be empty")
|
||||
for index, item in enumerate(value):
|
||||
require_string(item, f"{path}[{index}]")
|
||||
|
||||
|
||||
def reject_markers(text: str, markers: list[str], path: str) -> None:
|
||||
lowered = text.lower()
|
||||
found = [marker for marker in markers if marker.lower() in lowered]
|
||||
require(not found, f"{path} leaks forbidden marker(s): {', '.join(found)}")
|
||||
|
||||
|
||||
def validate_packet(document: dict[str, Any], source: Path) -> None:
|
||||
base = str(source)
|
||||
require(document.get("schema_version") == "ghostlight.responder_packet.v0", f"{base}.schema_version is wrong")
|
||||
require_keys(
|
||||
document,
|
||||
[
|
||||
"packet_id",
|
||||
"review_status",
|
||||
"fixture_lane",
|
||||
"canon_status",
|
||||
"coordinator_artifact_ref",
|
||||
"projected_context_ref",
|
||||
"responder_agent_id",
|
||||
"visible_context",
|
||||
"output_contract",
|
||||
"hidden_context_audit",
|
||||
"isolation_requirements",
|
||||
"packet_prompt_text",
|
||||
"review",
|
||||
],
|
||||
base,
|
||||
)
|
||||
require(document["review_status"] in REVIEW_STATUSES, f"{base}.review_status is invalid")
|
||||
require(document["fixture_lane"] in FIXTURE_LANES, f"{base}.fixture_lane is invalid")
|
||||
require(document["canon_status"] in CANON_STATUSES, f"{base}.canon_status is invalid")
|
||||
for key in ["packet_id", "coordinator_artifact_ref", "projected_context_ref", "responder_agent_id", "packet_prompt_text"]:
|
||||
require_string(document[key], f"{base}.{key}")
|
||||
require((ROOT / document["coordinator_artifact_ref"]).exists(), f"{base}.coordinator_artifact_ref does not exist")
|
||||
require((ROOT / document["projected_context_ref"]).exists(), f"{base}.projected_context_ref does not exist")
|
||||
|
||||
visible = document["visible_context"]
|
||||
require_keys(visible, ["local_context_prompt", "observed_event", "allowed_action_labels", "source_excerpts"], f"{base}.visible_context")
|
||||
require_string(visible["local_context_prompt"], f"{base}.visible_context.local_context_prompt")
|
||||
require_string_array(visible["allowed_action_labels"], f"{base}.visible_context.allowed_action_labels", nonempty=True)
|
||||
|
||||
event = visible["observed_event"]
|
||||
require_keys(event, ["event_id", "actor_agent_id", "observable_action", "spoken_text", "visible_object_refs", "visibility_notes"], f"{base}.visible_context.observed_event")
|
||||
for key in ["event_id", "actor_agent_id", "observable_action"]:
|
||||
require_string(event[key], f"{base}.visible_context.observed_event.{key}")
|
||||
require(isinstance(event["spoken_text"], str), f"{base}.visible_context.observed_event.spoken_text must be a string")
|
||||
require_string_array(event["visible_object_refs"], f"{base}.visible_context.observed_event.visible_object_refs")
|
||||
require_string_array(event["visibility_notes"], f"{base}.visible_context.observed_event.visibility_notes", nonempty=True)
|
||||
|
||||
excerpts = visible["source_excerpts"]
|
||||
require(isinstance(excerpts, list), f"{base}.visible_context.source_excerpts must be an array")
|
||||
for index, excerpt in enumerate(excerpts):
|
||||
path = f"{base}.visible_context.source_excerpts[{index}]"
|
||||
require_keys(excerpt, ["ref_id", "source_ref", "text"], path)
|
||||
for key in ["ref_id", "source_ref", "text"]:
|
||||
require_string(excerpt[key], f"{path}.{key}")
|
||||
|
||||
contract = document["output_contract"]
|
||||
require_keys(contract, ["response_schema", "required_fields", "response_rules"], f"{base}.output_contract")
|
||||
require_string(contract["response_schema"], f"{base}.output_contract.response_schema")
|
||||
require_string_array(contract["required_fields"], f"{base}.output_contract.required_fields", nonempty=True)
|
||||
require_string_array(contract["response_rules"], f"{base}.output_contract.response_rules", nonempty=True)
|
||||
|
||||
audit = document["hidden_context_audit"]
|
||||
require_keys(audit, ["omitted_context_refs", "forbidden_inference_classes", "known_absences"], f"{base}.hidden_context_audit")
|
||||
for key in ["omitted_context_refs", "forbidden_inference_classes", "known_absences"]:
|
||||
require_string_array(audit[key], f"{base}.hidden_context_audit.{key}", nonempty=True)
|
||||
|
||||
isolation = document["isolation_requirements"]
|
||||
require_keys(isolation, ["isolation_method", "packet_only", "no_repo_access", "no_conversation_context", "no_hidden_state_refs"], f"{base}.isolation_requirements")
|
||||
require(isolation["isolation_method"] in ISOLATION_METHODS, f"{base}.isolation_requirements.isolation_method is invalid")
|
||||
for key in ["packet_only", "no_conversation_context", "no_hidden_state_refs"]:
|
||||
require(isolation[key] is True, f"{base}.isolation_requirements.{key} must be true")
|
||||
require(isinstance(isolation["no_repo_access"], bool), f"{base}.isolation_requirements.no_repo_access must be boolean")
|
||||
|
||||
prompt_text = document["packet_prompt_text"]
|
||||
reject_markers(prompt_text, RAW_STATE_MARKERS, f"{base}.packet_prompt_text")
|
||||
reject_markers(prompt_text, HIDDEN_LEAK_MARKERS, f"{base}.packet_prompt_text")
|
||||
require(document["visible_context"]["local_context_prompt"] in prompt_text, f"{base}.packet_prompt_text must contain local context prompt")
|
||||
require(event["observable_action"] in prompt_text, f"{base}.packet_prompt_text must contain observed event")
|
||||
|
||||
review = document["review"]
|
||||
require_keys(review, ["reviewer", "review_notes", "accepted_for_sandbox_use", "failure_labels"], f"{base}.review")
|
||||
require_string(review["reviewer"], f"{base}.review.reviewer")
|
||||
require(isinstance(review["review_notes"], list), f"{base}.review.review_notes must be an array")
|
||||
require(isinstance(review["accepted_for_sandbox_use"], bool), f"{base}.review.accepted_for_sandbox_use must be boolean")
|
||||
require_string_array(review["failure_labels"], f"{base}.review.failure_labels")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Validate Ghostlight responder packets.")
|
||||
parser.add_argument("paths", nargs="*", type=Path, help="Responder packet JSON files to validate.")
|
||||
args = parser.parse_args()
|
||||
|
||||
paths = args.paths or DEFAULT_PACKETS
|
||||
require(paths, "no responder packets found")
|
||||
for path in paths:
|
||||
validate_packet(load_json(path), path)
|
||||
print(f"ok: {path}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Loading…
Reference in a new issue