Skip to content

LOOP Continuity: durable run state (ADR 0002, all phases P0-P4) - #1

Open
killerdasher wants to merge 12 commits into
mainfrom
continuity
Open

killerdasher wants to merge 12 commits into
mainfrom
continuity

Conversation

@killerdasher

Copy link
Copy Markdown
Owner

LOOP Continuity - durable run state across sessions (ADR 0002)

Implements the owner-approved architecture review (2026-11-05, "APPROVED WITH AMENDMENTS") across all phases:

  • P0 - docs/adr/0002-loop-continuity.md decision record with all seven amendments folded in; ADR 0001 addendum distinguishing skill-first architecture from helper-backed persistence.
  • P1 - scripts/continuity.mjs: optional, zero-external-dependency helper (Node >= 16.7). Schema v1 (six run statuses + separate integrity dimension), documented transition table with invalid transitions as hard errors, sanitized remote-URL identity fingerprints, atomic persistence (temp + fsync + rename, recoverable temps, repair only removes provably superseded leftovers), corrupt/unsupported state never silently overwritten, cancel/complete archiving with retained terminal evidence, best-effort secret scanner with documented limits. Identity-enforcement gap found by the real-module tests before any command shipped and fixed.
  • P2 - four thin commands (/loop-status, /loop-pause, /loop-resume, /loop-cancel) with the full resume-gate sequence (integrity, identity, no automatic resume, read-only reconciliation, explicit operator approval, restore at the recorded stage); no --force anywhere. SKILL.md Stage 0 checkpoint detection + Stage 8 archive rule. New reference references/continuity.md. Installer helper copy outside the skill dir (slice-identity checks stay exact) + uninstall.
  • P3 - nine new defect-injection mutations for every new structural check; real-module tests for stale integrity and undocumented integrity values.
  • P4 - docs/continuity.md with the ADR 0002 section 7 capability table verbatim; README command block, limitations, structure/docs rows; architecture section; CHANGELOG [Unreleased]; validator docs anchors + 3 more mutations.

Acceptance verification (owner checklist, real implementation only)

  • Invalid JSON rejected without data loss: continuity:corrupt-* (refuse-not-wipe, byte preservation, transition BLOCKED, restoral after repair).
  • Unsupported schema versions rejected and preserved: continuity:unsupported-schema-*.
  • Invalid transitions fail (never silent no-ops): continuity:invalid-transitions-error.
  • Secret-shaped values refused by the implemented scanner, limits documented: continuity:secret-shaped-values-refused, continuity:update-refuses-secret-patch, validator continuity:limits-documented + mutation.
  • Different project identity refused: continuity:identity-mismatch-refused (this check proved the P1 gap).
  • Stale integrity persisted/rejected: continuity:stale-integrity-persisted, continuity:undocumented-integrity-rejected.
  • Completed runs archived: continuity:cancel-archives, continuity:archive-retained-terminal-evidence.
  • Atomic-write recovery tested: continuity:atomic-write-no-temp-on-success, continuity:interrupted-temps-reported, continuity:repair-removes-superseded-temps-only.
  • No mandatory plugin/daemon/database/dependency: helper content checks (zero-dep, no-network) + mutations; install story unchanged except helper copy.
  • Backward-compatible install: existing install scenarios unchanged and extended (helper byte-identity on install, removal on uninstall).
  • Windows + Linux: CI runs the full suite on both.

Gates

node scripts/validate.mjs --all 178/178 (123 structural + 39 mutations + 16 installer scenarios) and node tests/harness/selftest.mjs 113/113, git diff --check clean.

No release is published here; merge gates CI, not a version bump.

…endum

ADR 0002: durable run state (.loop/continuity.json), four lifecycle
commands, resume gates, and the amended constraints from the 2026-11-05
owner architecture review:
- optional zero-dep helper scripts/continuity.mjs; BLOCKED degradation;
  skill-first workflow unchanged without it (ADR 0001 addendum)
- run status ACTIVE|PAUSED|RECOVERY_REQUIRED|BLOCKED|COMPLETED|CANCELLED
  with separate integrity VALID|SUSPECTED_STALE|CORRUPT
- no automatic resume; read-only reconciliation first; interrupted stage
  -> RECOVERY_REQUIRED; no bounded-loss promise
- concurrency is an operational rule; generation detects stale snapshots,
  not locking; no --force in the first release
- privacy: .loop/ git-excluded by instruction, no tokens/env values,
  best-effort secret scan, sanitized remote-URL fingerprints
- atomic writes (tmp+rename) and retained .loop/archive/ terminal runs
- verbatim capability-boundary table for docs

Gates: validate --all 123/123, selftest 79/79, git diff --check clean.
…erage

scripts/continuity.mjs (optional, zero-external-dep Node >=16.7 helper):
- schema v1: six run statuses + separate integrity dimension, allowlisted
  fields, stopReason retained as terminal evidence (ADR 0002 field list
  updated)
- documented transition table; invalid/unknown/terminal transitions are
  hard errors (never silent no-ops)
- sanitized remote-URL identity fingerprints (userinfo stripped before
  hashing; only scheme/host/path enters the hash)
- atomic persistence: <target>.tmp-<generation> + fsync + rename;
  left-behind temps reported by inspect and reconciled by 'repair' only
  when provably superseded (unparseable temps are refused)
- corrupt / unsupported-schema state: read-only, never overwritten;
  transitions and updates refuse with BLOCKED class (CLI exit 2)
- cancel/complete archive to .loop/archive/<taskId>.json; live file only
  removed after successful archive; archives never auto-deleted
- best-effort secret scan fail-closed on match, limits documented in the
  file header and enforced by a validator check
- defect found by test & fixed: identity mismatch is now mandatory for
  every state-mutating op (expectedRoot defaults to projectRoot)

Installers (ps1+sh): copy the helper to <config>/scripts/continuity.mjs
(outside skill dir, preserving skill-dir identity checks) and remove it
on uninstall.

validate.mjs: required-file entry, helper-copy installer markers, and
new content checks (zero-dep, no-network, status vocabulary, documented
secret-scan limits). selftest.mjs: 32 new checks importing the REAL
module + real CLI exit-code contract (0/1/2/3), fake secret shapes
assembled at runtime so repo text never carries a matchable pattern.

Gates: validate --all 128/128, selftest 111/111, git diff --check clean.
- four thin commands: /loop-status (read-only inspect + git reality
  cross-check), /loop-pause (ACTIVE-only, honest boundary language),
  /loop-resume (full gate order: integrity/identity/no-auto-resume/
  read-only reconciliation/operator approval, then restore into the
  nine-stage loop at the recorded stage), /loop-cancel (cancel+archive,
  never deletes terminal evidence). All route through the continuity
  helper, report BLOCKED with exact reasons when it cannot run, and
  never offer --force (the one legitimate mention is the negative
  statement, which the new validator check strips before scanning).
- SKILL.md: Stage 0 detects live checkpoints and refuses a silent second
  run (write-before-act when helper available, BLOCKED otherwise);
  Stage 8 records completion through the helper and archives;
  PAUSED-not-COMPLETED on early stops; reference pointer added to the
  reporting-contract list.
- new reference: skills/loop-engine/references/continuity.md (mechanism
  honesty table verbatim from ADR 0002 SS7, helper search paths with
  degradation contract, state vocabulary, write protocol, resume gates).
- validate.mjs: required-file + reference-list entries; per-command
  structural checks (frontmatter, description, \, continuation
  reference route, BLOCKED language, no-force) and installer matrix
  hardening: fresh-global-install now verifies helper byte-identity,
  uninstall-removes-with-backup verifies helper removal.

Gates: validate --all 162/162 (119+27+16), selftest 111/111.
- 9 new defect-injection mutations, each pinned to a check that was only
  a print in P2: continuity-command argument placeholder, BLOCKED path,
  --force offering (strips the negation sentence properly), helper
  non-builtin import, helper network primitive, status vocabulary,
  secret-scan limitation language, installer helper-copy lines (all
  helper plumbing, not just the copy step - a partial removal slipped
  past the mention-marker and that hole is documented in the mutation),
  and continuity reference deletion. Mutation count 27 -> 36.
- two new real-module integrity checks: SUSPECTED_STALE persists through
  opUpdate; undocumented integrity values are rejected by the schema.

Gates: validate --all 171/171 (119+36+16), selftest 113/113.
- docs/continuity.md: user-facing guide carrying the ADR 0002 SS7
  capability table verbatim, command table, gate sequence, atomic-write
  and privacy chapters, and the four honest limitation anchors
  (no guaranteed checkpoints, no bounded-loss promise, no locking,
  no --force) enforced by the validator.
- README: continuity command block, limitations entry, repository-tree
  and docs-table rows, check-count counter now reads 280+ (171
  validator + 113 selftest actual).
- docs/architecture.md: three-tier continuity section.
- CHANGELOG: [Unreleased] documents the whole Continuity feature set,
  the identity-enforcement fix from P1, skill touchpoints, and the
  validation growth.
- validator: docs:continuity-table (verbatim SS7 anchors),
  docs:continuity-limits, docs:readme-continuity + 3 new mutations
  (helper tier drift, promise inflation, README command removal).

Gates: validate --all 178/178 (123+39+16), selftest 113/113,
git diff --check clean.
P0 finalization pass (ADR-file-only scope, no functionality changes):
- section 2 contradiction resolved: corrupt state is preserved in place
  byte-for-byte; the earlier 'renamed *.corrupt-<ts>' example becomes an
  explicit-operator-only action
- full implemented transition table added (14 pairs incl. block/unblock/
  recovery-flag from PAUSED; terminal-state transitions are errors)
- exact schema v1 field table matching the helper validator (settings,
  stopReason, recovery shape, advisory createdBy)
- command contracts (preconditions/mutations/refusals) for the four
  commands
- acceptance criteria mapped to the implemented checker IDs
- P1-P3 plan annotated with actual ship status on branch continuity
  (P1 90d7cec, P2 e691a06, P3 a6b7560, P4 905f5e7; PR #1, CI 42209037515
  green, not released; main untouched until merge)
- parked decisions recorded: --force/supersede, Case J, corrupt-file
  relocation helper, archive pruning
- migration risks and maintenance costs documented

Gates: validate --all 178/178, selftest 113/113, git diff --check clean.
Fresh P1 spec-vs-implementation review (9 items) found one real gap:
taskId was format-checked on load but drifted/hand-edited IDs were
accepted. Hardened into an enforced derivation invariant:

- validateStateObject recomputes taskIdFor(objective, createdAt) and
  rejects mismatch as invalid -> inspectState classifies CORRUPT,
  all state-mutating ops refuse (transition blocked), corrupted
  bytes preserved in place (never silently reset)
- opUpdate already validates the merged state pre-write, so objective
  patches that would break the derivation are refused before persist
- tests: continuity:task-identity-invariant-holds (derivation round-
  trips) + continuity:task-identity-tamper-rejected (objective
  tampered ON DISK -> CORRUPT + blocked transition + bytes equal)

No command or skill changes (out of P1 scope). Gates: validate --all
178/178, selftest 115/115 (113 + 2 new), git diff --check clean.
…pause

Fresh P2 spec-vs-implementation review of the four lifecycle commands
and skill integration (all items verified shipped: e691a06):
- one LOW finding closed: loop-pause.md carried a stale
  'RELAUNCH-IN-PROGRESS' template note naming a state that does not
  exist in the six-state vocabulary; reworded to 'Status gate:'
- loop.md intentionally unchanged per ADR 0002 file structure (thin
  /loop router; continuity touchpoints live in SKILL.md Stage 0/8 +
  references/continuity.md; validator mutations pin this)

Gates: validate --all 178/178, selftest 115/115, git diff --check clean.
New scripts/evaluator.mjs (zero-external-dep, Node >= 16.7):
- evidence manifest .loop/verification.json (schema v1, atomic writes via
  the continuity helper's writeJsonAtomic; appends via 'record --from-stdin')
- criteria MUST/SHOULD/COULD with optional deliverable artifacts; evidence
  verdicts; exec entries = deterministic (argv, no shell) + advisory entries
  cap at NEEDS_REVIEW; findings with severities; blockers
- outcomes PASS | FAIL | BLOCKED | NEEDS_REVIEW (exit 0/1/2/3), fail-closed
  precedence: FAIL decisive; missing/invalid/corrupt/secret-flagged manifest,
  zero criteria, unresolved blockers, open CRITICAL/HIGH, uncovered MUST ->
  BLOCKED; advisory-only MUST / uncovered SHOULD -> NEEDS_REVIEW
- --fresh re-executes every deterministic PASS command and lets the real
  exit code override recorded claims (tampered verdicts exposed)
- binds to the live continuity task's taskId; refuses cross-run and
  identity-mismatched planted manifests (fail-closed)
- no external evaluator model, no paid API, nothing remote

Integration (nine-stage methodology unchanged):
- SKILL.md Stage 8 completion-verdict touchpoint; references/continuity.md
  completion-evidence protocol; docs/evaluator.md guide; CHANGELOG entry
- validator: 8 new evaluator:checks (implementation, vocabulary, exit
  contract, skill touchpoint, reference section, docs guide, changelog,
  nine-stages-intact)

Tests: selftest 131/131 (16 new evaluator checks: positive pass/binding/
fresh-rerun/recovery, negative fail/missing/advisory/blockers/recover,
adversarial tamper-exposure/cross-task/foreign-identity/no-criteria/
corrupt-preserved, CLI secret-refused/help-contract). validate --all 187/187.
New scripts/profiles.mjs (zero-external-dep) + five modular references
under references/profiles/ (build, debug, refactor, audit, migrate):

- every profile defines the same seven dimensions: required inputs,
  relevant analysis stages, verification methods, risk priorities,
  completion criteria, forbidden shortcuts, expected evidence
- selection is explicit user intent only: --profile <name> on /loop,
  resolved deterministically (case/space tolerant); unknown names
  refused with the valid list - never silently defaulted - and the run
  continues as the generic nine-stage loop with the correction stated
- task scope never overridden: only the --profile flag is stripped,
  every other byte of the task text (incl. other flags, line breaks,
  whitespace runs) is preserved verbatim; last flag wins with a
  correction note; a valueless --profile is refused
- universal safeguards bound in every reference (anchor validated) and
  by the SKILL.md dispatch: nine stages + hard safety rules unchanged;
  profiles only add specificity, never remove a stage or loosen the
  Stage 8 evidence requirements
- AUDIT is read-only by design: fixes are a separate, explicitly
  authorized re-scope, never a silent side effect
- resolver: resolve/strip/task CLI, exit 0/4; findings fixed in-run:
  resolveFromTask field mismatch (would have silently gone generic -
  the failure class the resolver exists to prevent), verbatim
  whitespace semantics now asserted by the tests

Integration: loop.md flag docs, SKILL.md Stage 0 dispatch note,
docs/profiles.md, CHANGELOG; validator +10 profiles:checks (5 refs
pinned for all seven dimensions + anchor, resolver, loop flag, skill
dispatch, docs guide, changelog).

Tests: selftest 143/143 (12 new checks incl. adversarial unknown-name,
missing-value, last-wins, CLI contract, verbatim scope). validate --all
203/203.
Evidence-backed evaluation of LOOP-compatible models on identical tasks
in controlled environments. scripts/model-eval.mjs (zero-dep) measures
the eight P6 dimensions from completed evaluation records; the task set
is version-pinned (v1.0.0) and hash-pinned into every record, so
comparisons across differing task sets are refused. No ranking is
derivable by construction: comparisons emit per-dimension observed
counts with an explicit non-ranking disclaimer; profiles emit counts
plus a sample-size classification (INSUFFICIENT_SAMPLE below 3 tasks
per dimension, never scored), and completion requires an explicit
limitations statement. Fail-closed records: unknown-field refusal,
secret-shape scanning on every write, atomic writes, deterministic
runIds with duplicate refusal, completed records immutable. Runs are
operator-gated: no credentials handled or stored, no provider called or
switched. Core tests validate the framework offline on fixture records
(no paid API). Token-gated limit disclosed: zero real model profiles
ship - producing one requires an operator-initiated run.

Gates: validate --all 213/213 (158 structural + 39 mutations + 16
installer), selftest 156/156 (13 new model-eval checks + 1 immutability
check), git diff --check clean.
Ten-step, evidence-driven improvement system for LOOP's own artifacts:
scripts/evolution.mjs collects sanitized evaluation outcomes (validated
continuity archives + evaluator manifests) into an append-only ledger
(history never rewritten; integrity-suspect sources refused), classifies
weaknesses into instructions/references/validation/recovery with a
versioned keyword classifier and a recurring threshold, and refuses
proposals that do not map to recorded failed evaluation cases.
Candidates are verified in detached git worktrees under the OS temp dir
(the active skill and repository are never touched; .opencode data dirs
are refused outright); the full regression suite runs on both sides with
no shell, and comparison is deterministic per Summary tier: any
regression hard-rejects the candidate (REJECTED is final and unapprovable,
evidence preserved). Adoption requires a measurable improvement (strictly
more passing checks, zero regressions) plus an explicit --approved-by,
recorded as a decision only - appliedByTool is always false, nothing is
published automatically. Model claims are carried but labeled
evidence:false and never influence verdicts. Records are schema-pinned
v1, content-hash ids, secret-scanned, atomically written. Honest scope:
LOOP does not learn model weights and holds no permanent autonomous
intelligence - evolution means human-approved, regression-gated artifact
changes.

Gates: validate --all 225/225 (170 structural + 39 mutations + 16
installer), selftest 168/168 (12 new evolution checks incl. real
worktree E2E, regression rejection, approval gate, traversal refusal),
git diff --check clean.
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