Sign in to edit tickets from this page.

← all tickets · home

Add scenario archiving to the consumer MCP surface

resolved d2664ce7-9d02-44ec-8587-c7048c6ba2b3

created_at
2026-04-30
updated_at
2026-05-01
priority
P1
ticket_type
feature
resolved_at
2026-05-01
resolution
accepted

Body

Add scenario archiving to the consumer MCP surface

Summary

Add a new consumer MCP tool named:

archive_scenario

This tool marks a scenario as archived. Archived scenarios remain available for audit, provenance, lineage, existing worlds, and hash-based inspection. Archived scenarios disappear from the default active scenario catalogue and cannot seed new worlds or new scenario forks.

This ticket follows the current Chukwa tool-surface standard: consumer tools must be strict, self-documenting, and aligned with handler/store behavior. The consumer MCP manifest remains the source of truth for how to call Chukwa without operator access or source-code spelunking.


Product decision

archive_scenario means:

Move a scenario out of the active scenario catalogue while preserving its row, content-addressed components, lineage, and existing-world usability.

Archived scenarios:

do not appear in list_scenarios by default
do appear in list_scenarios with include_archived=true
remain readable by exact hash
remain visible in lineage/provenance reads
do not seed new worlds
do not serve as fork parents
continue to support existing worlds that already reference their hash

There is no explicit unarchive tool.

Reassembling a scenario that computes the same hash as an archived scenario makes that scenario active again.


Non-goals

Do not physically remove scenario rows.

Do not physically remove component rows.

Do not physically remove lineage rows.

Do not physically remove worlds, turns, attempts, audit events, source invocations, chunks, artifacts, or token records.

Do not add a force flag.

Do not add archive-by-name.

Do not add an unarchive tool.

Do not add a new operator tool.

Do not create a separate guide.

Do not loosen schemas to make this pass.


Consumer-facing tool

Add this consumer MCP tool:

archive_scenario

Input:

{
  "hash": "64_character_lowercase_hex_scenario_hash",
  "dry_run": false,
  "reason": "optional human-readable reason"
}

Rules:

hash is required.
hash must be a 64-character lowercase hex scenario hash.
dry_run defaults to false.
reason is optional.
reason is trimmed.
empty reason becomes null.
reason has maxLength 1024.
unknown fields are rejected.
archiving by name is not supported.
archiving by scenario_ref is not supported.
archiving an already archived scenario fails with ARCHIVED_SCENARIO.
archiving a scenario with active worlds succeeds.
archiving a scenario with children succeeds.
archiving a scenario with parents succeeds.

Existing active worlds must not block archiving. Archiving is a catalogue/state transition for the scenario, not a world operation.


MCP manifest changes

Add archive_scenario to:

CONSUMER_TOOLS
ALL_TOOLS
tools/call dispatch
consumer tool manifest entry builder
manifest example registry
executable manifest example harness

Place it in the scenario section after:

children_of

and before:

create_world

The consumer scenario section order becomes:

list_scenarios
get_scenario
lineage_of
children_of
archive_scenario
create_world

Use this exact manifest description:

Purpose: Archive a stored scenario so it leaves the active scenario catalogue.

Use when: You have an exact scenario hash that should no longer appear in list_scenarios by default and should no longer seed new worlds or forks.

Input: hash is the exact 64-character scenario hash. dry_run previews the archive operation without mutating. reason records an optional human-readable archive reason.

Returns: dry_run previews return the target scenario, active_world_count, child_count, has_parents, and archivable. Successful archive calls return status archived, archived_at, archive_reason, scenario_slug, active_world_count, child_count, and has_parents.

Next: Call list_scenarios with include_archived=true to confirm the archived scenario, or call assemble_scenario to create a new active scenario.

Notes: This tool archives the scenario row. It preserves worlds, turns, audit events, lineage rows, and content-addressed components. Existing worlds that already reference this scenario continue to read their stored scenario hash. Archiving is by hash only because scenario slugs can repeat across distinct scenario hashes.

Use this exact input schema:

{
  "type": "object",
  "properties": {
    "hash": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$",
      "description": "Exact scenario hash to archive."
    },
    "dry_run": {
      "type": "boolean",
      "default": false,
      "description": "Preview the archive operation without mutating the store."
    },
    "reason": {
      "type": "string",
      "maxLength": 1024,
      "description": "Optional human-readable reason recorded with the archived scenario."
    }
  },
  "required": ["hash"],
  "additionalProperties": false
}

Add a manifest example using dry-run only:

{
  "description": "Preview archiving the scenario assembled in recipe C.",
  "arguments": {
    "hash": "$scenario_hash",
    "dry_run": true,
    "reason": "scenario no longer belongs in the active catalogue"
  }
}

Do not add a mutating archive example to the executable manifest recipe path.


Storage model changes

Add scenario archival state to the scenario store.

Add this enum:

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ScenarioStatus {
    Active,
    Archived,
}

Add these fields to StoredScenario:

pub status: ScenarioStatus,
pub archived_at: Option<DateTime<Utc>>,
pub archive_reason: Option<String>,

Add these fields to ScenarioSummary:

pub status: ScenarioStatus,
pub archived_at: Option<DateTime<Utc>>,
pub archive_reason: Option<String>,

Add this result type:

#[derive(Debug, Clone)]
pub struct ArchivedScenarioSummary {
    pub hash: String,
    pub scenario_slug: String,
    pub status: ScenarioStatus,
    pub created_at: DateTime<Utc>,
    pub archived_at: DateTime<Utc>,
    pub archive_reason: Option<String>,
    pub child_count: u32,
    pub has_parents: bool,
}

Add this trait method to ScenarioStore:

async fn archive_scenario(
    &self,
    hash: &str,
    reason: Option<String>,
) -> Result<ArchivedScenarioSummary, StoreError>;

Add this store error:

#[error("scenario `{0}` is archived")]
ArchivedScenario(String),

Map it to MCP error code:

ARCHIVED_SCENARIO

Database migration

Add the next migration.

Create scenario archival status and archive metadata:

CREATE TYPE scenario_status AS ENUM ('active', 'archived');

ALTER TABLE scenarios
    ADD COLUMN status scenario_status NOT NULL DEFAULT 'active',
    ADD COLUMN archived_at TIMESTAMPTZ,
    ADD COLUMN archive_reason TEXT,
    ADD CONSTRAINT scenarios_archived_state_check
        CHECK ((status = 'archived') = (archived_at IS NOT NULL));

CREATE INDEX scenarios_status_created_idx
    ON scenarios(status, created_at DESC, hash ASC);

CREATE INDEX scenarios_status_slug_created_idx
    ON scenarios(status, scenario_slug, created_at DESC, hash ASC);

CREATE INDEX scenarios_archived_at_idx
    ON scenarios(archived_at DESC)
    WHERE status = 'archived';

Do not alter component tables.

Do not alter world tables.

Do not alter lineage tables except where query projections need scenario status fields.


Postgres implementation

Update scenario loading so every scenario row projection selects:

status
archived_at
archive_reason

Populate these fields in:

StoredScenario
ScenarioSummary
lineage response scenario objects
children response scenario objects

load_scenario_by_hash must return active and archived rows.

get_scenario_by_hash must return active and archived rows.

get_scenario_by_name must resolve active scenarios only:

WHERE scenario_slug = $1
  AND status = 'active'
ORDER BY created_at DESC, hash ASC
LIMIT 1

scenario_hash_for_ref must accept active scenarios only.

Hash refs must check row status:

missing row -> StoreError::NotFound
active row -> accepted
archived row -> StoreError::ArchivedScenario(hash)

Name refs already resolve active scenarios only.

Implement archive_scenario as an update.

Use a transaction.

Lock the target scenario row:

SELECT hash, scenario_slug, status::text, created_at
FROM scenarios
WHERE hash = $1
FOR UPDATE

Behavior:

missing row -> StoreError::NotFound("scenario hash <hash>")
archived row -> StoreError::ArchivedScenario(hash)
active row -> archive the row

Update statement:

UPDATE scenarios
SET status = 'archived',
    archived_at = now(),
    archive_reason = $2
WHERE hash = $1
  AND status = 'active'
RETURNING hash, scenario_slug, status::text, created_at, archived_at, archive_reason

Load child_count and has_parents using the existing lineage count helpers.

Return ArchivedScenarioSummary.


Memory implementation

Update the in-memory scenario row type with:

status: ScenarioStatus,
archived_at: Option<DateTime<Utc>>,
archive_reason: Option<String>,

New scenario rows start as:

status: ScenarioStatus::Active,
archived_at: None,
archive_reason: None,

load_scenario_by_hash returns active and archived rows.

get_scenario_by_hash returns active and archived rows.

get_scenario_by_name considers active rows only.

list_scenarios respects ListFilter.include_archived.

scenario_hash_for_ref accepts active rows only and returns StoreError::ArchivedScenario(hash) for archived hash refs.

Implement archive_scenario by mutating the row:

row.status = ScenarioStatus::Archived;
row.archived_at = Some(Utc::now());
row.archive_reason = reason;

Return ArchivedScenarioSummary.


List filter changes

Add this field to ListFilter:

pub include_archived: bool,

Default:

include_archived: false

list_scenarios returns active scenarios by default.

When include_archived is false, filter to:

s.status = 'active'

When include_archived is true, return active and archived scenarios.

Add top-level include_archived to the list_scenarios MCP input schema:

{
  "include_archived": {
    "type": "boolean",
    "default": false,
    "description": "Also include scenarios archived by archive_scenario."
  }
}

Keep include_archived at the top level of list_scenarios arguments. Do not put it inside filter.

Update handle_list_scenarios to read the top-level argument and populate ListFilter.include_archived.


Reactivation by identical scenario content

Reassembling or re-persisting a scenario that computes the same hash as an archived scenario must make that scenario active again.

Update persist_manifest in both memory and Postgres.

When the computed scenario hash already exists and the existing row is archived, update that row:

status = active
archived_at = NULL
archive_reason = NULL

Keep the same scenario hash.

Keep:

was_new_scenario = false

Add this field to AssembleResult:

pub unarchived_scenario: bool,

Set:

true when an existing archived scenario hash became active again
false otherwise

Expose it from assemble_result_to_value:

{
  "unarchived_scenario": true
}

This applies to both assemble_scenario and fork_scenario responses because both use scenario persistence.


MCP handler behavior

Add:

async fn handle_archive_scenario(args: &Value, env: &McpEnv) -> Result<Value, McpError>

Start the handler by rejecting unknown keys.

Allowed keys:

hash
dry_run
reason

Parse:

hash: required 64-character lowercase hex
dry_run: optional bool, default false
reason: optional string, trimmed, empty string becomes None, maxLength 1024

Load the scenario by hash:

env.scenario_store.get_scenario_by_hash(&hash)

Behavior:

missing scenario -> UNKNOWN_HASH
archived scenario -> ARCHIVED_SCENARIO
active scenario -> continue

Count active worlds referencing the scenario hash:

ListWorldsFilter {
    include_deleted: false,
    scenario_hash: Some(hash.clone()),
    ..Default::default()
}

Expose this as:

active_world_count

Active worlds do not block archiving.

Dry-run response for an active scenario:

{
  "message": "[dry_run] Would archive scenario \"ant_on_plate\" (<hash>). No store mutation.",
  "dry_run": true,
  "archivable": true,
  "hash": "<hash>",
  "scenario_slug": "ant_on_plate",
  "status": "active",
  "active_world_count": 1,
  "child_count": 0,
  "has_parents": false
}

Successful archive response:

{
  "message": "Scenario \"ant_on_plate\" (<hash>) archived.",
  "dry_run": false,
  "hash": "<hash>",
  "scenario_slug": "ant_on_plate",
  "status": "archived",
  "archived_at": "2026-04-30T00:00:00Z",
  "archive_reason": "scenario no longer belongs in the active catalogue",
  "active_world_count": 1,
  "child_count": 0,
  "has_parents": false
}

Update create_world

create_world must reject archived scenarios.

Hash path:

get_scenario_by_hash(hash)
missing -> UNKNOWN_HASH
archived -> ARCHIVED_SCENARIO
active -> continue

Name path:

get_scenario_by_name(name)
missing -> UNKNOWN_NAME
active -> continue

Update world_store::resolve_scenario_ref in both memory and Postgres.

Hash refs must reject:

StoredScenario.status == ScenarioStatus::Archived

Add WorldStoreError::ScenarioArchived(String).

Map it to MCP error code:

ARCHIVED_SCENARIO

Do not reject archived scenarios in existing-world runtime paths.

Existing worlds already store a scenario hash. Runtime reconstitution, turn reads, state reads, existing-world inspection, and existing-world history must continue to load archived scenarios by exact hash.

Only new world creation is blocked.


Update fork_scenario

fork_scenario must reject archived primary parents and archived additional parents.

Use scenario_hash_for_ref for the primary parent.

For additional_parents, load each parent hash and check status.

Archived parent error:

ARCHIVED_SCENARIO

Do not allow archived scenarios to participate in new scenario derivation.


Update read serialization

Update stored_scenario_to_value to include:

{
  "status": "active",
  "archived_at": null,
  "archive_reason": null
}

For archived scenarios:

{
  "status": "archived",
  "archived_at": "2026-04-30T00:00:00Z",
  "archive_reason": "scenario no longer belongs in the active catalogue"
}

Update list_scenarios rows to include the same fields.

list_scenarios default response returns active rows only.

list_scenarios with include_archived=true returns active and archived rows.

get_scenario by exact hash returns archived scenarios.

get_scenario by name resolves active scenarios only.


Lineage behavior

lineage_of and children_of must preserve archived scenarios in provenance responses.

Returned scenario objects include:

status
archived_at
archive_reason

Do not filter archived scenarios out of lineage results.

Archiving changes active catalogue status. It does not rewrite provenance.


Error mapping

Add MCP mapping:

StoreError::ArchivedScenario(_) -> ARCHIVED_SCENARIO
WorldStoreError::ScenarioArchived(_) -> ARCHIVED_SCENARIO

Use these exact error meanings:

UNKNOWN_HASH: no scenario row exists for the provided hash.
UNKNOWN_NAME: no active scenario exists for the provided scenario slug.
ARCHIVED_SCENARIO: the scenario row exists and is archived, or an archived scenario was supplied where an active scenario is required.
BAD_ARG: input shape, type, hash format, reason length, or unknown-field error.

Response shape contract

archive_scenario dry-run response fields:

message
dry_run
archivable
hash
scenario_slug
status
active_world_count
child_count
has_parents

archive_scenario success response fields:

message
dry_run
hash
scenario_slug
status
archived_at
archive_reason
active_world_count
child_count
has_parents

Do not include component counts.

Do not include world summaries.

Do not include lineage arrays.

Keep this tool focused.


Tests to add

Manifest test

Assert:

archive_scenario appears in CONSUMER_TOOLS.
archive_scenario appears in ALL_TOOLS.
archive_scenario appears in tools/list.
archive_scenario appears after children_of and before create_world.
archive_scenario input schema has hash, dry_run, and reason.
archive_scenario input schema has additionalProperties: false.
archive_scenario input schema does not accept name.
archive_scenario input schema does not accept scenario_ref.
archive_scenario example uses dry_run=true.

Unknown field test

This fails with BAD_ARG:

{
  "hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "banana": true
}

Unknown hash test

This fails with UNKNOWN_HASH:

{
  "hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}

Dry-run test

Assemble a scenario.

Call:

{
  "hash": "$scenario_hash",
  "dry_run": true,
  "reason": "cleanup"
}

Assert:

dry_run is true
archivable is true
status is active
scenario remains visible in default list_scenarios
create_world from the hash succeeds

Successful archive test

Assemble a scenario with no worlds.

Call:

{
  "hash": "$scenario_hash",
  "reason": "cleanup"
}

Assert:

status is archived
archived_at is present
archive_reason is cleanup
default list_scenarios does not include the scenario
list_scenarios include_archived=true includes the scenario
get_scenario by hash returns the scenario with status archived
get_scenario by name does not return the archived scenario
create_world by hash fails with ARCHIVED_SCENARIO

Already archived test

Archive the same scenario twice.

Second call fails with:

ARCHIVED_SCENARIO

Active-world preservation test

Assemble a scenario.

Create a world from it.

Call archive_scenario.

Assert success.

Assert response includes:

active_world_count = 1
status = archived

Assert the existing world still appears in list_worlds.

Assert get_world for the existing world succeeds.

Assert creating a new world from the archived scenario hash fails with:

ARCHIVED_SCENARIO

Create-world protection test

Archive a scenario.

Call create_world with:

{
  "scenario_ref": {
    "hash": "$scenario_hash"
  },
  "slug": "archived_scenario_world"
}

Assert:

ARCHIVED_SCENARIO

Call create_world with:

{
  "scenario_ref": {
    "name": "$scenario_slug"
  },
  "slug": "archived_scenario_world_by_name"
}

Assert:

UNKNOWN_NAME

Fork protection test

Archive a scenario.

Call fork_scenario with the archived scenario as primary parent.

Assert:

ARCHIVED_SCENARIO

Call fork_scenario with the archived scenario as an additional parent.

Assert:

ARCHIVED_SCENARIO

Lineage preservation test

Create a parent scenario.

Fork a child scenario.

Archive the parent.

Call lineage_of for the child hash.

Assert the parent appears with:

status = archived
archived_at present
archive_reason present when provided

Call children_of for the parent hash.

Assert the child appears.

Unarchive-by-reassembly test

Assemble a scenario.

Archive it.

Assemble the exact same scenario again.

Assert:

same scenario hash
was_new_scenario = false
unarchived_scenario = true
status = active
archived_at = null
archive_reason = null
create_world succeeds

Store tests

Add memory and Postgres tests for:

archive_scenario marks scenario archived
archive_scenario rejects unknown hash
archive_scenario rejects already archived hash
list_scenarios hides archived rows by default
list_scenarios include_archived returns archived rows
get_scenario_by_hash returns archived rows
get_scenario_by_name ignores archived rows
scenario_hash_for_ref rejects archived hash refs
persist_manifest unarchives an identical archived hash

Serialization tests

Assert stored_scenario_to_value emits:

status
archived_at
archive_reason

Assert default active scenarios serialize as:

{
  "status": "active",
  "archived_at": null,
  "archive_reason": null
}

Assert archived scenarios serialize as:

{
  "status": "archived",
  "archived_at": "<timestamp>",
  "archive_reason": "<reason or null>"
}

Update stale-language tests

Extend the consumer manifest stale-language test.

Reject these strings anywhere in consumer manifest JSON:

delete_scenario
deleted scenario
scenario deletion
tombstone
hard delete
force delete

Keep existing stale-surface rejections from the tool-surface cleanup.

The scenario lifecycle term exposed to consumers is:

archive
archived
archiving

Definition of done

The ticket is complete when:

archive_scenario exists on the consumer MCP surface.
archive_scenario is self-documenting in tools/list.
archive_scenario accepts only hash, dry_run, and reason.
archive_scenario rejects unknown fields.
archive_scenario archives by hash only.
scenario rows have active/archived status.
archived scenarios disappear from default list_scenarios.
list_scenarios include_archived=true returns archived scenarios.
get_scenario by hash returns archived scenarios.
get_scenario by name resolves active scenarios only.
create_world rejects archived scenarios.
fork_scenario rejects archived parent scenarios.
existing worlds can still load and inspect their stored scenario by hash.
lineage_of and children_of preserve archived scenarios in provenance.
archive_scenario succeeds even when active worlds reference the scenario.
assembling identical scenario content makes the scenario active again.
assemble_scenario and fork_scenario responses expose unarchived_scenario.
manifest examples still execute.
cargo test passes.

Bottom-line instruction: implement scenario archiving as a strict consumer MCP lifecycle operation. Preserve content-addressed history. Remove archived scenarios from active use. Keep existing worlds and provenance intact.

Proposed resolution

Implemented scenario archiving as a strict consumer MCP lifecycle operation.

Compliance / verification matrix:

RequirementStatusEvidence
Add consumer archive_scenario tool in scenario section after children_of and before create_worldCompleteAdded to CONSUMER_TOOLS, ALL_TOOLS, dispatch, manifest builder, examples, and executable example harness. Live /mcp tools/list reports archive_scenario present.
Use the exact requested manifest description and strict input schemaCompleteManifest description follows the requested Purpose/Use/Input/Returns/Next/Notes text. Live schema requires only hash; properties are hash, dry_run, reason; additionalProperties is false.
Reject archive-by-name, archive-by-scenario-ref, unknown fields, invalid hash, invalid/long reasonCompleteMCP boundary validation accepts only hash, dry_run, and reason; hash uses ^[a-f0-9]{64}$; reason is trimmed, empty becomes null, max 1024. Tests cover bad unknown fields and unknown hash.
Add dry-run-only manifest exampleCompleteManifest example previews archiving with $scenario_hash, dry_run: true, and a reason. Mutating archive was not added to the executable recipe path.
Add scenario archival modelCompleteAdded ScenarioStatus, archive fields on StoredScenario and ScenarioSummary, ArchivedScenarioSummary, ListFilter.include_archived, StoreError::ArchivedScenario, and ScenarioStore::archive_scenario.
Add Postgres migrationCompleteAdded migrations/0007_scenario_archiving.sql with scenario_status, status, archived_at, archive_reason, the archived-state check constraint, and the requested indexes. Deployed repo zip contains the migration.
Implement memory store archive behaviorCompleteNew scenarios start active; archive mutates status/archive metadata; default lists hide archived; include_archived returns them; hash reads still work; name reads active-only; hash refs for active-use paths reject archived rows; identical reassembly reactivates archived hashes.
Implement Postgres store archive behaviorCompleteScenario projections select status/archive fields; archive_scenario uses a transaction and row lock; active-only name/ref resolution is enforced; hash reads include archived; identical persist reactivates archived hashes.
Update list_scenariosCompleteTop-level include_archived added to schema/handler. Default list returns active rows; include_archived=true includes archived rows. Rows serialize status, archived_at, and archive_reason.
Reactivation by identical scenario contentCompletepersist_manifest in memory and Postgres reactivates existing archived hashes, clears archive metadata, keeps was_new_scenario=false, and exposes unarchived_scenario from assemble/fork responses.
Add MCP archive handler and response shapesCompletehandle_archive_scenario returns the required dry-run and success fields, including active_world_count, child_count, and has_parents, with no component counts, world summaries, or lineage arrays.
Block new world creation from archived scenariosCompletecreate_world and memory/Postgres world-store scenario resolution map archived hashes to ARCHIVED_SCENARIO; name path resolves active scenarios only. Existing-world inspection paths continue to load by stored hash.
Block scenario forks from archived parentsCompletePrimary parent resolution uses active-only refs; additional parent hashes load and reject archived scenarios. Acceptance tests cover archived primary and additional parent rejection.
Preserve lineage/provenance and existing worldsCompleteget_scenario by hash returns archived scenarios; lineage_of and children_of preserve archived scenario objects with status/archive fields; archiving with active worlds succeeds and reports active_world_count.
Error mappingCompleteStoreError::ArchivedScenario and WorldStoreError::ScenarioArchived map to ARCHIVED_SCENARIO; unknown hash/name and bad args retain the requested meanings.
Stale-language protectionsCompleteConsumer manifest stale-language tests reject deletion/tombstone/force-delete lifecycle terms while allowing archive terminology.
No non-goal additionsCompleteNo physical deletes, no component/world/lineage removal, no force flag, no archive-by-name, no unarchive tool, and no new operator tool were added.
Timeline requested by userCompleteWork timing is documented at /tmp/d2664ce7-9d02-44ec-8587-c7048c6ba2b3_timeline.md with start, stop, duration, elapsed, activity, and time-driver notes.

Verification receipts:

CheckResult
cargo test --lib --features test-fixtures in rust:1.88-bookwormPassed: 620 passed; 0 failed.
cargo test --tests --features test-fixtures in rust:1.88-bookwormPassed across lib, binary, and integration targets.
cargo test --tests --features test-fixtures,postgres-tests -- --test-threads=1Passed against postgres://postgres:postgres@127.0.0.1:5433/postgres; DB-backed suite reported 779 passed; 0 failed, with integration targets completed.
Post-final test-only acceptance filtersPassed: cargo test --lib --features test-fixtures archive_scenario -- --nocapture reported 6 passed; cargo test --lib --features test-fixtures,postgres-tests archive_scenario -- --test-threads=1 --nocapture against the same sacrificial DB reported 7 passed.
git diff --checkPassed.
Deploymentbash k8s/deploy.sh completed; rollout succeeded and pod is Ready.
Live healthhttps://chukwa.benac.dev/healthz returns ok.
Live consumer MCP contracttools/list on https://chukwa.benac.dev/mcp reports 33 consumer tools, includes archive_scenario, required fields ['hash'], properties ['dry_run', 'hash', 'reason'], and list_scenarios.include_archived.
Deployed source bundlehttps://chukwa.benac.dev/chukwa-repo.zip contains repo/migrations/0007_scenario_archiving.sql, repo/src/mcp.rs with archive_scenario and include_archived, and repo/src/mcp/tests.rs with the final archive acceptance tests.

Commits pushed to main:

Note: after the final test-only commit, I reran targeted archive acceptance filters rather than the full suite again. The full lib, integration, and Postgres suites passed immediately before that final test-only addition, and the deployed artifact includes the final tests.

History (4 events)

Sign in as a human to drive this ticket from the page, or use the MCP tools.