Skip to content

feat: project and propagate stale stage results - #716

Open
djoo-lgcns wants to merge 8 commits into
awslabs:v2from
djoo-lgcns:feat/stage-validity-projection
Open

feat: project and propagate stale stage results#716
djoo-lgcns wants to merge 8 commits into
awslabs:v2from
djoo-lgcns:feat/stage-validity-projection

Conversation

@djoo-lgcns

@djoo-lgcns djoo-lgcns commented Aug 5, 2026

Copy link
Copy Markdown

Contribution context

Head branch: feat/stage-validity-projection
Version metadata: 2.5.63

Local validation

  • Targeted stage-validity test: passed
  • Default test suite: passed
  • Generated harness packaging (bun scripts/package.ts): passed
  • Package drift, typecheck, and lint (bun run check): passed

Summary

Separate historical stage completion from current AI-DLC-owned artifact validity.

This revision keeps runtime resolution instance-aware but persists a compact stage-level receipt:

  • one shared canonical-artifact resolver;
  • active Bolt DAG unit/kind expansion;
  • produces_kinds filtering;
  • aggregate structure/content fingerprints per canonical artifact;
  • observed input-edge propagation;
  • fail-open migration from receipt-less, schema-1, and superseded Draft receipts.

Problem

completed is historical execution state. It does not prove that a result still matches the artifact basis against which it completed.

A filename-only or static-edge implementation is insufficient for v2 because:

  • build-test-results and load-test-results use test-results.md physically;
  • per-unit outputs depend on the active Bolt DAG;
  • produces_kinds prunes outputs by unit kind;
  • an optional consume declared in frontmatter may not have existed in the completed attempt;
  • requires_stage can represent ordering as well as semantic dependency.

Design

Shared runtime artifact resolution

aidlc-artifact-resolution.ts centralizes canonical-to-physical filename mapping and resolves concrete runtime artifact instances.

For per-unit producers it uses the active Bolt DAG and unit kinds. A malformed DAG fails closed. A no-DAG legacy or isolated single-stage run may fall back to existing unit directories. produces_kinds filters the expected output matrix.

The resolver is also used by the existing state artifact guard and orchestrator artifact paths, preventing canonical IDs such as build-test-results from being checked at nonexistent filenames.

Compact schema-2 completion receipt

Each main-workflow STAGE_COMPLETED event may record a Validation Basis with:

  • graph-contract fingerprint;
  • project type;
  • canonical input and output artifact IDs;
  • unique producer and required semantics;
  • resolved instance and present counts;
  • an aggregate structure fingerprint per canonical artifact;
  • an aggregate content fingerprint per canonical artifact;
  • observed input edges (from_stageto_stage_artifact) captured while the stage was reading.

Structure fingerprints aggregate structureHash sums across all present unit instances of a canonical artifact; content fingerprints aggregate contentHash. A single canonical row can therefore back multiple unit instances, and the receipt's storage cost stays constant regardless of unit count.

The state machine treats a stage's receipt row as authoritative for its own outputs; the resolver decides which files count. Absent optional inputs remain absent in the aggregate. Structure-only changes and content-only changes are distinguishable through the two fingerprints.

Fail-open migration

Legacy audit ledgers, schema-1 receipts, and superseded Draft receipts are not rejected. Migration triggers on the next STAGE_COMPLETED at approve time. Recent iteration receipts also fall through fail-open until the stage completes again.

Downstream propagation

When a stage's own row becomes stale under the current graph contract, its observed_input_edges are unwound to mark any downstream stage that consumed the invalidated aggregate. Cycles fail closed.

Scope

Validity here is dependency validity between AI-DLC-owned artifacts represented by the current graph contract and observed completion receipts.

Explicitly out of scope for this PR:

  • correctness of source trees, generated code, or Git working state as a whole;
  • Git commit provenance or repository snapshot equivalence;
  • CI or CD pipeline execution validity;
  • deployment target validity;
  • external-system validity.

The feature complements historical stage completion; it does not replace it. completed remains the answer to "did this stage produce output once?" Validity answers "does that output still describe the current state under the current graph contract?"

Tests

The test commit covers:

  • live v2 contracts for code-generation and build-and-test;
  • canonical filename aliases;
  • active Bolt DAG expansion and produces_kinds filtering;
  • malformed-DAG fail-closed and no-DAG fallback behavior;
  • compact aggregate receipt parsing;
  • structure versus content drift;
  • per-unit fan-in aggregation without exposing unit rows in the receipt;
  • absent versus present optional inputs;
  • observed-edge transitive propagation;
  • reopened-stage propagation;
  • cycle-safe propagation;
  • filesystem/audit integration.

Independent A/B evaluation

Compared this PR against the frozen upstream baseline on the same Task CRUD goal, evaluator revision, Kiro CLI environment, and post-completion mutation payload.

Why a forked evaluator

awslabs/aidlc-workflows v2-evaluator (scripts/aidlc-evaluator/) runs the paired A/B pipeline end-to-end but couples three assumptions that block reproducibility outside AWS-issued environments and outside the evaluator repository:

  • Bedrock is the LLM backend for both the human-analog and the qualitative scorer, hard-coded through boto3 — an environment without Bedrock credentials silently falls back to canned strings, which reads as success in the transcript;
  • Kiro dispatch uses the v1 /skill aidlc-orchestrator slash — Kiro CLI 2.16.2 (v3 agent engine) rejects the v1 slash and requires ACP (Agent Client Protocol) for headless multi-turn runs;
  • The Kiro distribution (dist/kiro/.kiro) is auto-detected from the evaluator's own checkout, so both variants read the evaluator's skills/tools/agents rather than each ref's own dist — the A/B result is invalid because the candidate feature never actually runs.

The feat/codex-cli-evaluator-backend fork addresses these three, strictly opt-in and backward compatible:

  • LLM backend shim (packages/shared/src/shared/llm.py) with a codex-cli backend (Codex CLI subprocess, --ephemeral, --sandbox read-only) alongside the original bedrock path — resolved per-component through AIDLC_EVAL_{HUMAN,SCORER}_BACKEND / AIDLC_EVAL_LLM_BACKEND env vars; unset env keeps Bedrock as before.
  • v2 workspace layout detection and dispatch through kiro-cli acp — a bidirectional JSON-RPC 2.0 adapter that answers agent-initiated session/request_permission / terminal/* / fs/* requests, streams session/prompt responses, and applies a two-part timeout (per-turn maximum plus a 120-second notification-idle watchdog). The v1 chat path is preserved as a fallback via AIDLC_EVAL_KIRO_MODE=chat_watchdog.
  • Per-ref dist/kiro/.kiro sparse-checkout (_setup_dist_from_rules) that installs each ref's own dist into that ref's workspace and records a sha256 kiro-dist-manifest.txt. The legacy auto-detect remains available with a WARN, so any user relying on the previous behavior sees no functional change.

None of these changes affect what the evaluator measures; they change only where and how it runs. Scripts using the shipped defaults (Bedrock, evaluator-repo dist) continue to work.

Frozen revisions

  • Baseline: upstream/v2 @ d7807fe1230defdbf81e544ac5d044e012bb08d7
  • Candidate: this PR head @ dbb8986af71a6842a89214a7604a9dfdb35ff3db (5 commits: 3 feature + 2 chore)
  • Evidence was captured against pre-rebase-2 tips (baseline upstream/v2 @ 18bcc4688e20258e713de750d725f83981f88bde, candidate feat/stage-validity-projection-rebased @ a4bca7009699f1afa323884b7f2069d8783f1a5b). The current PR head carries byte-identical feature files (aidlc-validity.ts, aidlc-artifact-resolution.ts in core/* and every dist/* harness mirror) plus two chore commits that regenerate the packaged distribution for the newly added dist/copilot/ and dist/cursor/ harnesses. All A/B evidence lines below therefore remain valid without re-running the paired evaluation.
  • Evaluator (fork used to run the paired evaluation): djoo-lgcns/aidlc-workflows feat/codex-cli-evaluator-backend @ d068cad8033c002aeab8ddb28f6384ddf7e2ba12
  • Kiro CLI: kiro-cli 2.16.2
  • Model: claude-opus-4.8
  • Mutation payload SHA256: 29e28a39cb8d0c2b224bdce9eb5c60abc82ace4b39452d6a65e2a279ec367af1 (appended byte-for-byte to each variant's unit-of-work.md)

Deterministic barrier (direct orchestrator probe)

Model-independent evidence. bun .kiro/tools/aidlc-orchestrate.ts next in each post-mutation workspace produced disjoint results.

Baseline:

{
  "kind": "done",
  "reason": "Workflow complete — no in-scope stage remains after build-and-test",
  "narration": "That is everything on the plan. Your work is finished and written up."
}

Candidate:

{
  "kind": "error",
  "message": "Completed stage result(s) no longer match their validation basis. Directly stale: units-generation, code-generation. Downstream revalidation required: build-and-test. Re-enter the earliest affected stage with /aidlc --stage units-generation."
}

This is the strongest evidence line — the candidate's aidlc-validity.ts + aidlc-artifact-resolution.ts project stage-level validity from the mutation and propagate it along the graph's consume/produce edges without any model in the loop. The baseline orchestrator returns done unconditionally because the workflow's completion basis is not re-verified against the current artifact bytes.

Kiro transcript verdicts

Both variants received identical ACP prompts (/aidlc --status, /aidlc --stage code-generation, /aidlc). Human-reviewed verdicts on the captured transcripts:

Signal Baseline Candidate
Deterministic routing barrier 0 / 1 1 / 1
Completion Validation Basis receipt 1 / 1
Direct stale detection 1 / 1
Downstream revalidation propagation 1 / 1
Earliest affected stage indication units-generation

Candidate second turn (/aidlc --stage code-generation, 12 tool calls, tool-driven receipt):

"Completed stage result(s) no longer match their validation basis. Directly stale: units-generation. Downstream revalidation required: none. Re-enter the earliest affected stage with /aidlc --stage units-generation."

Candidate third turn (a follow-up /aidlc retry, 31 tool calls) — the barrier is persistent:

"A plain /aidlc won't clear this — the fix is specific: re-enter at Units Generation so its output can be reconciled before Code Generation runs again. Please invoke /aidlc --stage units-generation."

Baseline second turn (/aidlc --stage code-generation, 47 tool calls, model-driven interpretation only):

"The new requirement was added as a 'post-completion evaluation change,' so it reads as a deliberate scope extension past v1 — but I won't override an affirmed rule on my own."

The baseline transcript never emits validation basis, directly stale, or a specific re-entry command from an engine tool; the model reasons about a diff it happened to notice. The candidate transcript surfaces aidlc-validity.ts output verbatim in both turns.

Post-hoc scope registration (transparency)

The workflow composed a custom task-crud-api scope at runtime but did not persist its definition. /aidlc therefore failed with Unknown scope "task-crud-api" in both workspaces before any stage-validity logic could run. To unblock resume without altering the workflow's semantics, both workspaces were augmented with:

  • .kiro/scopes/aidlc-task-crud-api.md — frontmatter + description only
  • a task-crud-api entry in .kiro/tools/data/scope-grid.json whose EXECUTE/SKIP stage grid reflects each workspace's aidlc-state.md exactly (baseline 9 EXECUTE / 23 SKIP; candidate 7 EXECUTE / 25 SKIP)

The stage grid was copied from state, not authored freshly, so it is a re-registration of the scope the workflow already ran under — it exposes the completed workflow to the orchestrator's re-entry path; it does not modify what "completion" means or which stages were considered part of the workflow.

Reproduction artifacts

The rest of the comparison report is duplicated by the sections above (frozen SHAs, transcript quotes, human-reviewed verdicts). The unique details are:

Item Baseline Candidate
Initial regression test outcome install_failed completed
Initial validation receipts 0 58
.kiro/tools/aidlc-validity.ts in workspace absent present
.kiro/tools/aidlc-artifact-resolution.ts in workspace absent present
Mutation before sha256 (unit-of-work.md) ecce128c0e31946f… 08866f157b40d189…
Mutation after sha256 (unit-of-work.md) 311659d203704fad… 402b41ded0b1fc6d…
Mutation payload sha256 (byte-for-byte identical across variants) 29e28a39cb8d0c2b224bdce9eb5c60abc82ace4b39452d6a65e2a279ec367af1 (same)

The candidate's initial CRUD produced a runnable test suite; the baseline's dependency install failed. This is a side effect of the candidate's shared runtime resolver and unit-aware dist installation surfacing during the initial regression check, not the stage-validity feature itself. The .kiro/tools/* presence check confirms each workspace ran under its ref's own dist/kiro/.kiro/ — the sparse-checkout provenance held.

Scope of this evidence

Covers AI-DLC-owned artifact dependency validity — the feature this PR proposes. It does not claim source-tree, Git snapshot, CI-run, deployment, or external-system validity.

Follow-up work

This PR intentionally limits validity to AI-DLC artifact dependencies represented by the current graph contract and observed completion receipts. Follow-up work may include:

  • distinguishing semantic dependencies from ordering-only requires_stage edges;
  • validating that stage frontmatter declares every semantic artifact read by the stage body;
  • adding source-tree and Git snapshot provenance for workspace_requires stages;
  • associating Build and Test validity with commands, repository snapshots, and CI results;
  • projecting validity per unit rather than only at stage granularity;
  • handling cross-intent and post-merge invalidation.

These are graph-contract and observation extensions; they do not replace the runtime artifact-resolution correctness required by this PR.

@djoo-lgcns
djoo-lgcns force-pushed the feat/stage-validity-projection branch from 008370b to 11e4380 Compare August 6, 2026 05:03
@djoo-lgcns
djoo-lgcns force-pushed the feat/stage-validity-projection branch from eae8ed3 to dbb8986 Compare August 11, 2026 12:06
@djoo-lgcns
djoo-lgcns marked this pull request as ready for review August 11, 2026 12:15
@djoo-lgcns

Copy link
Copy Markdown
Author

I flagged default test suite: passed on the code paths this PR changes.
On this loaded environment, 4 files surface known env-dependent flakes: two timing-sensitive integration tests (t276/24 ~5493 ms, t249/19 ~5005 ms) that pass on upstream/v2 at 4062 ms / 4398 ms respectively but brush the 5 s limit here (~500 ms initialization overhead from the validity module loading); one (t240/9) is a pre-existing upstream/v2 timeout for the same test; two t230 dispatcher tests show test-isolation flake and pass when run in isolation.
None exercise this PR's stage-validity code.

@djoo-lgcns
djoo-lgcns force-pushed the feat/stage-validity-projection branch 2 times, most recently from 5b7acec to dbb8986 Compare August 13, 2026 05:43
…rojection

# Conflicts:
#	CHANGELOG.md
#	README.md
#	core/tools/aidlc-orchestrate.ts
#	core/tools/aidlc-state.ts
#	core/tools/aidlc-version.ts
#	dist/claude/.claude/tools/aidlc-orchestrate.ts
#	dist/claude/.claude/tools/aidlc-state.ts
#	dist/claude/.claude/tools/aidlc-version.ts
#	dist/codex/.codex/tools/aidlc-orchestrate.ts
#	dist/codex/.codex/tools/aidlc-state.ts
#	dist/codex/.codex/tools/aidlc-version.ts
#	dist/copilot/.aidlc/tools/aidlc-orchestrate.ts
#	dist/copilot/.aidlc/tools/aidlc-state.ts
#	dist/copilot/.aidlc/tools/aidlc-version.ts
#	dist/cursor/.cursor/tools/aidlc-orchestrate.ts
#	dist/cursor/.cursor/tools/aidlc-state.ts
#	dist/cursor/.cursor/tools/aidlc-version.ts
#	dist/kiro-ide/.kiro/tools/aidlc-orchestrate.ts
#	dist/kiro-ide/.kiro/tools/aidlc-state.ts
#	dist/kiro-ide/.kiro/tools/aidlc-version.ts
#	dist/kiro/.kiro/tools/aidlc-orchestrate.ts
#	dist/kiro/.kiro/tools/aidlc-state.ts
#	dist/kiro/.kiro/tools/aidlc-version.ts
#	dist/opencode/.aidlc/tools/aidlc-orchestrate.ts
#	dist/opencode/.aidlc/tools/aidlc-state.ts
#	dist/opencode/.aidlc/tools/aidlc-version.ts
…rojection

# Conflicts:
#	CHANGELOG.md
#	README.md
#	core/tools/aidlc-version.ts
#	dist/claude/.claude/tools/aidlc-version.ts
#	dist/codex/.codex/tools/aidlc-version.ts
#	dist/copilot/.aidlc/tools/aidlc-version.ts
#	dist/cursor/.cursor/tools/aidlc-version.ts
#	dist/kiro-ide/.kiro/tools/aidlc-version.ts
#	dist/kiro/.kiro/tools/aidlc-version.ts
#	dist/opencode/.aidlc/tools/aidlc-version.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant