Workflow Artifact Lifecycle
Flow Agents treats task artifacts as useful working memory, not permanent product documentation. Feature branches should promote durable planning, decisions, evidence pointers, and acceptance notes into normal project docs, source, schemas, or provider records instead of carrying .kontourai/flow-agents/ runtime files.
The local artifact root is a current-state dashboard first and a short-lived recovery cache second. It should answer “what needs attention now?” without forcing agents to sift through old successful deliveries.
Audit Command
Use the read-only cleanup audit before making any local retention decision:
npm run workflow-artifact-cleanup-audit -- --artifact-root .flow-agents
npm run workflow-artifact-cleanup-audit -- --artifact-root .flow-agents --json
The default root for local runtime artifacts is .kontourai/flow-agents. .flow-agents may hold explicit durable Flow Agents config/install state, but it is not a runtime fallback. Move old local sessions with the migration script instead of relying on automatic fallback reads.
The command scans immediate workflow directories, skips non-workflow lanes such as archive/, and reports active WIP separately from cleanup candidates, terminal done records, active learning follow-ups, and invalid sidecars.
Use the Current-State Semantics and Local Retention Policy sections below to interpret each bucket. In particular, learning records with learning.status: followup_required or any routing[].status: open remain active learning follow-ups until every route is completed, opened elsewhere, deferred with a trigger, accepted, or rejected.
Apply Mode
Dry-run classification is the default and remains a zero-side-effect, read-only pass. Pass --apply to actually archive-move the sessions the classifier already puts in cleanup_candidate (and aged terminal_done sessions past the retention window) out of the active listing:
# Dry-run (unchanged; still the default with no flags)
npm run workflow-artifact-cleanup-audit -- --artifact-root .kontourai/flow-agents
# Archive-move cleanup_candidate / aged terminal_done sessions
npm run workflow-artifact-cleanup-audit -- --artifact-root .kontourai/flow-agents --apply
# Also archive-move lifecycle-ambiguous and genuinely-stub invalid sessions (second gate)
npm run workflow-artifact-cleanup-audit -- --artifact-root .kontourai/flow-agents --apply --apply-ambiguous
# Custom retention window and destination
npm run workflow-artifact-cleanup-audit -- --artifact-root .kontourai/flow-agents \
--apply --freshness-window-hours 72 --archive-root /path/to/archive
Two-gate model. --apply alone only ever moves cleanup_candidate sessions and aged terminal_done sessions (the same buckets the manual Session Archival Policy procedure in context/contracts/artifact-contract.md already selects by hand) — it never touches anything the classifier calls active_wip, active_learning_followup, or invalid. A session the classifier marks invalid with an “unrecognized lifecycle shape” reason (a delivered/accepted session whose shape doesn’t fit a known terminal pattern, but is otherwise a real, substantive record) is reported separately as ambiguous and is moved only when --apply-ambiguous is also passed — this is a deliberate second opt-in, not a default, because confirming that kind of straggler is genuinely done (e.g. its referenced PR actually merged) is a human/flow judgment call this tool does not make for you (see --confirm below).
Safety rails. Regardless of flags, --apply/--apply-ambiguous never touch:
- Active work —
active_wipandactive_learning_followupsessions are hard-excluded; no flag combination overrides this. - A session someone else is holding — if the local liveness stream shows a fresh claim on a session’s slug (any actor, not just the current one), that session is skipped and reported as blocked by a held liveness claim, even if the classifier would otherwise archive it.
- Anything too recent — a session newer than the freshness window (
--freshness-window-hours, default 48) is never moved, usingstate.json’supdated_atas the primary signal; directory modification time is used only as a fallback whenstate.jsonis missing or unparsable, never preferred overupdated_atwhen it is present (bulk worktree operations can reset file mtimes without touching the actual session content, so mtime alone is not a trustworthy freshness signal). - Infrastructure directories — per-agent claim/assignment tracking, the runtime adapter’s generated output, the skills catalog, and similar structural directories are recognized by their own shape (they carry their own top-level pointer/lock files, i.e. they are themselves a workflow-sidecar-managed root, not a session) and are never treated as sessions to begin with, so a nested runtime tree with real sub-sessions of its own is never flattened into a single misclassified entry or partially swept.
- Substantive sessions with a schema nit — an
invalidsession that actually has realstate.jsonpluslearning.jsonand/ortrust.bundlecontent is always report-only, even under--apply --apply-ambiguous; only a genuinely empty/stubinvaliddirectory (no state at all, or state with no sidecar content) is ever eligible for the ambiguous-archive path.
Never delete. Every move is an archive-move (rename, or copy-then-verify-then-remove-source when a plain rename can’t cross filesystems) — the tool has no delete capability of any kind, in any mode.
Where things land. --apply moves matching sessions to <archive-root>/<date>-<runid>/<slug>/, where --archive-root defaults to a sibling .kontourai/flow-agents-archive/ next to the artifact root. This is a deliberately different destination from the manual, in-tree <artifact-root>/archive/<slug>/ convention the Session Archival Policy section of context/contracts/artifact-contract.md documents for hand-driven moves: giving every automated --apply invocation its own dated, run-scoped directory means two same-day runs never collide, and it keeps the artifact root’s own scan set immutable while a run is in progress. A human archiving a session by hand may still use the in-tree archive/<slug>/ path from that section; the two destinations serve the same retirement intent through different (manual vs. automated) procedures — this mode automates the cleanup_candidate/aged-terminal_done selection that section already specifies, it does not replace the manual path for everything else.
Manifest. Any --apply run that moves at least one session writes MANIFEST.md into that run’s archive directory, listing each moved session’s slug, classification, last-updated timestamp, and the classifier’s own reason text. A run that moves nothing writes no manifest and creates no new archive directory. --confirm <slug>=<evidence> (repeatable) lets the invoking human or flow record, verbatim, evidence they already independently gathered (for example, that a straggler’s referenced PR is confirmed merged) into that slug’s manifest row — a slug moved without a matching --confirm entry shows "none recorded" rather than a fabricated value. This tool makes no network or gh calls itself; it only records evidence it is told.
This is the same procedure a 2026-07-05 live triage sweep of this repo’s own .kontourai/flow-agents/ tree ran by hand before --apply existed (see that sweep’s own MANIFEST.md for the real cases — stale stubs, aged cleanup candidates, confirmed-merged-PR stragglers, and the infrastructure/schema-nit exclusions above) — the sweep’s findings are what shaped the safety rails above; --apply automates exactly that already-proven procedure.
Artifact Lanes
Use one local non-durable lane under .kontourai/flow-agents/:
| Lane | Path | Commit Policy | Purpose |
|---|---|---|---|
| Runtime workspace | .kontourai/flow-agents/<slug>/ |
Do not commit | Local session state, sidecars, delegate events, scratch evidence, and recovery notes. |
The runtime workspace stays local because it may contain stale session state, machine-specific paths, or noisy intermediate artifacts. When a branch needs cross-session or cross-person traceability, promote the durable summary, decisions, evidence pointers, and acceptance notes into docs, source, schemas, or provider records instead of committing runtime artifacts.
Current-State Semantics
Treat state.json as the active-work signal for local users and pull-work.
| State shape | Meaning | Queue treatment |
|---|---|---|
planning, planned, in_progress, verifying, blocked, failed, not_verified, or needs_decision |
Work still needs agent or user attention. | Active WIP or shepherding candidate. |
verified with next_action.status: continue |
Local evidence passed, but release, final acceptance, or learning is not closed. | Active shepherding candidate. |
verified with next_action.status: done |
Evidence passed and the next phase was completed outside the state machine or by a provider record. | Cleanup candidate; should be advanced to a terminal state during final acceptance. |
accepted with phase: learning and learning.status: followup_required |
Learning was captured but at least one routed follow-up is still open or undecided. | Active learning follow-up until routed to backlog, docs, evals, skills, knowledge, or an explicit deferred trigger. |
delivered, accepted, or archived with phase: done, or accepted/archived with closed learning routing |
Completed local workflow. | Terminal only once a promotion claim is recorded (see Promote-Then-Archive Gate); a delivered/accepted session with no promotion claim is a cleanup candidate, not terminal. Retain only while useful for recovery or audit. |
verified is not a terminal state. It means the verifier supplied evidence. Final acceptance must still record the provider change, CI/release result, docs promotion decision, and any learning route before the workflow stops being active.
Promote-Then-Archive Gate
Archiving a delivered session is not a parallel chore to promoting its durable residue — it is gated on it. The sequence is:
final acceptance -> promote -> archive
Durable-residue extraction is the archival act. A delivered session’s decisions,
vocabulary, learnings, and doc updates must be promoted into durable living docs
(docs/decisions/<slug>.md, CONTEXT.md, docs/learnings/*, README.md,
context/contracts/, schemas, provider records) before the session is retired,
so no delivered work is retired without its knowledge extracted.
The promotion claim
The promote step records what was promoted where and writes a promotion
claim into the session trust.bundle:
# Real durable residue: each --evidence-path must exist on disk at record time.
flow-agents workflow-sidecar promote <session-dir> \
--evidence-path docs/decisions/<slug>.md \
--evidence-path CONTEXT.md
The claim is session-local by construction (check kind policy ->
policy_rule evidence, no command / execution.label). It therefore needs no
new reconcile-manifest entry and can never become a [not-run] /
unbacked-command divergence at CI trust-reconcile: the reconciler classifies it
session-local and accepts it as an ATTESTED claim. The durable doc paths are the
claim’s evidence refs; each is verified to exist on disk when the claim is
recorded (a missing path fails loud), and they are mirrored into an auditable
promotion.json in the session directory. The claim is detectable by the archive
gate and validators via claim.metadata.promotion without any manifest change.
Empty-promotion path
When a delivered session genuinely produced no durable residue (e.g. a pure refactor with no decision, vocabulary, or doc change), record an explicit, auditable no-residue promotion rather than skipping the gate:
flow-agents workflow-sidecar promote <session-dir> \
--none --reason "<why nothing durable was promoted>"
This still produces a promotion claim (with none: true and the reason), so the
decision that nothing needed promoting is recorded, not silently assumed.
Archive enforcement
workflow-artifact-cleanup-audit classifies a delivered or accepted session
that reached a terminal shape without a promotion claim as a
cleanup_candidate (blocked from archive) with a reason naming the promote
remedy — not terminal_done. With the claim (real residue or --none) it
classifies terminal_done and may be archived. Already-archived sessions are
past the gate and are never re-flagged; this gate is not a backfill of historical
archives.
Learning Closeout
Learning records are a routing surface, not a permanent parking lot.
Use learning.status: followup_required only while at least one learning route still needs action. Each route should end in one of these outcomes:
completed: the doc, eval, skill, backlog item, code change, or knowledge update was made.open: a provider-backed issue, backlog artifact, or named owner now tracks the follow-up.deferred: the follow-up has a concrete revisit trigger, such as a later milestone, repeated failure pattern, provider capability, or date.rejected: the follow-up was considered and intentionally not pursued, with a reason in the learning record.
Once every route is completed, open elsewhere, deferred with a trigger, or rejected with a reason, record learning.status: learned and advance the workflow out of active WIP. Do not leave local runtime state as needs_decision only because a durable follow-up issue exists.
Terminal learning review also records correction state in learning.json. Before closeout, compare intended behavior to observed behavior:
- Clean runs use
correction.needed: false, briefcorrection.evidence, and closed/no-follow-up routing such astarget: "none"withstatus: "completed". - Mismatches use
correction.needed: truewith typedcorrection.type, stablecorrection.recurrence_key, intended behavior, observed behavior, gap, and a prevention route or explicitno_change_rationale.
Correction records stay in local learning.json for this slice. They do not create a new sidecar, do not upload to Source/Sink storage, do not build Console/dashboard UI, and do not automatically open provider issues. Future consumers can derive correction rate, resolved corrections, repeated recurrence keys, stale unresolved corrections, and clean-run rate from the same fields.
Durable learning should be promoted by target:
- workflow rule changes go to
context/contracts/,skills/, or workflow docs - regression expectations go to
evals/ - product or architecture decisions go to
docs/ordocs/adr/ - executable work goes to GitHub issues or the configured backlog provider
- durable user/team memory goes to the configured knowledge store
Local Retention Policy
For local-only users, keep enough local state to recover recent work, but do not use .kontourai/flow-agents/<slug>/ as the long-term system of record.
Recommended defaults:
| Artifact class | Retain locally | Durable destination |
|---|---|---|
| Active WIP, blockers, and unresolved decisions | Until resolved | Current .kontourai/flow-agents/<slug>/ state and handoff. |
| Recently merged or accepted deliveries | 14-30 days, or until the next queue audit | PR body, issue comments, release records, promoted docs, or archived evidence refs. |
| Security, migration, release, or provider-governance evidence | 30-90 days when useful for audit | Provider record, release note, durable doc, or external evidence store. |
| Routine successful local runtime artifacts | Delete or archive after durable promotion and recovery window | Usually none beyond provider record and docs. |
| Learning records with routed follow-ups | Until all routes are completed, opened elsewhere, deferred with trigger, or rejected | Backlog issue, docs/evals/skills change, or knowledge note. |
When a future Source/Sink service is available, the same lifecycle should apply: local runtime artifacts become a cache and upload source; the service becomes the searchable history. Local-only users should still be able to run cleanup from provider records and durable docs without losing active work.
Prevention Rules
To prevent historical entries from polluting current-state scans:
- After a PR is merged or a no-provider-change path is accepted, final acceptance must advance
state.jsonout ofverifiedunless there is a real blocker. - If learning is required, route every learning item before marking the workflow inactive. Open durable issues are valid routes; they should not keep the local workflow active forever.
pull-workshould classify oldverifiedrecords withnext_action.status: doneas cleanup candidates, not active implementation work.- Queue audits should flag
needs_decisionorfollowup_requiredrecords older than the local recovery window. - Cleanup should preserve links to PRs, issues, durable docs, and evidence summaries before deleting or archiving local runtime folders.
Durable Closeout Shape
Durable closeout content is the handoff from working memory to project knowledge. Put it in the provider record, PR body, issue comments, release note, ADR, README section, schema docs, or runbook that owns the shipped behavior. It should record:
- shipped behavior or explicit non-shipped result
- provider change records such as PRs or issues
- verification evidence and residual gaps
- durable docs targets updated or intentionally skipped
- ADRs, README sections, schema docs, runbooks, or release notes created
- follow-up issues or learning-review records
- confirmation that
.flow-agents/runtime artifacts remain untracked
Completion Rule
Before merge to main:
- Promote durable behavior, contracts, decisions, operations notes, and usage guidance into long-lived docs such as
README.md,docs/,docs/adr/, schema docs, runbooks, changelogs, or provider records. - Make sure the durable record names the promotion targets and any accepted gaps.
- Confirm
.kontourai/flow-agents/runtime artifacts remain untracked. - Keep links to provider records, durable docs, or archived external evidence instead of relying on temporary local files.
main must not contain tracked files under .kontourai/flow-agents/. If runtime artifacts still seem necessary after merge, their durable content has not been promoted yet.
Promotion Targets
Promote by ownership:
- user-facing behavior:
README.md, product docs, or workflow usage docs - architecture and policy decisions:
docs/adr/or focused design docs - workflow rules and gates:
context/contracts/,skills/,agents/, and workflow docs - schemas and API contracts:
schemas/and contract docs - operational behavior: runbooks, release notes, or deployment docs
- evidence and release state: PR body, provider checks, release records, or durable evidence docs
- follow-up work: provider-backed issues or backlog artifacts
Do not promote raw intermediate thinking wholesale. Promote the resulting decisions, requirements, evidence, and user-facing instructions.
Enforcement
Runtime state remains ignored under .kontourai/flow-agents/. Static package validation fails if runtime artifacts are tracked. Reviewers should reject PRs that omit durable docs, source, schema, provider, or evidence updates needed to understand shipped behavior.