Skip to content

PR 007 — Autoflow State Machine: commit-bound, pure orchestration state - #9

Open
LogicDuke wants to merge 18 commits into
mainfrom
pr-007/autoflow-state-machine
Open

PR 007 — Autoflow State Machine: commit-bound, pure orchestration state#9
LogicDuke wants to merge 18 commits into
mainfrom
pr-007/autoflow-state-machine

Conversation

@LogicDuke

@LogicDuke LogicDuke commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Purpose

PR 007 implements the Autoflow engine's state model: the commit-bound record of what has been requested and what has been independently established for one unit of work.

trusted workflow binding + one already-normalized event
    -> immutable WorkflowState | rejection

It answers exactly one question:

Given everything recorded so far for this repository at this exact commit, is this event a legal thing to record, and what is the resulting state?

It does not answer what should happen next. Legality is domain; selection is policy, and policy belongs to a later PR.

The audit found that 12 of the 18 lifecycle distinctions exposed by the real PR 005/006 workflows are already owned by PR 003 (authority), PR 004 (freshness), PR 005 (findings), or PR 006 (claims). Only six are orchestration state, which is what keeps this layer small: 3 workflow statuses, 2 invocation states, 7 events, 16 rejection reasons.

Frozen scope

No I/O, and nothing is invoked. No agent execution, dispatch, or transport; no provider adapters; no GitHub/Claude/OpenAI/Gemini/CodeRabbit calls; no network, filesystem, subprocess, database, or Evidence Store persistence; no clock, timer, or identifier generation; no Promises or async of any kind. Both exported functions are pure functions of their arguments.

PR 007 consumes only the outputs of PR 004, PR 005, and PR 006. There is no signature that accepts an AgentReport, a ReviewSubmission, or an EvidenceRecord, so a second normalizer is a compile-time impossibility rather than a review comment. Frozen vocabulary constants are imported from those layers — redeclaring FRESHNESS.CURRENT would create a divergent second answer — but no reader, normalizer, or validator function is.

Nothing in PR 001–006 changed behaviour. Only src/domain/index.ts (export block), tests/domain/reader-parity.test.ts (two-way → three-way), and README.md (one paragraph) are modified.

The state machine

WorkflowStatus   OPEN | AWAITING_HUMAN_DECISION | CLOSED
InvocationState  REQUESTED | REPORTED
WorkflowClosure  HUMAN_DECISION_RECORDED | CALLER_CLOSED
Events           INVOCATION_REQUESTED | INVOCATION_REPORTED | REVIEW_ADMITTED
                 EVIDENCE_ADMITTED | HEAD_OBSERVED | HUMAN_GATE_OPENED | CLOSE_REQUESTED

Governing principle: recording a fact is legal whenever the workflow is not closed; initiating work is not.

Event OPEN AWAITING_HUMAN_DECISION CLOSED
INVOCATION_REQUESTED applied WORKFLOW_AWAITING_HUMAN WORKFLOW_CLOSED
INVOCATION_REPORTED applied applied WORKFLOW_CLOSED
REVIEW_ADMITTED applied applied WORKFLOW_CLOSED
EVIDENCE_ADMITTED, kind ≠ human-decision applied applied, status unchanged WORKFLOW_CLOSED
EVIDENCE_ADMITTED, kind = human-decision applied, stays OPEN applied, clears the gate → OPEN WORKFLOW_CLOSED
HEAD_OBSERVED, different commit applied, revision + 1 applied, revision + 1, clears the gate → OPEN WORKFLOW_CLOSED
HEAD_OBSERVED, same commit HEAD_UNCHANGED HEAD_UNCHANGED WORKFLOW_CLOSED
HUMAN_GATE_OPENED applied → AWAITING_HUMAN_DECISION HUMAN_GATE_ALREADY_OPEN WORKFLOW_CLOSED
CLOSE_REQUESTED applied → CLOSED applied → CLOSED WORKFLOW_CLOSED

CLOSED is absolutely terminal: no reopen, no resurrection. A new unit of work is a new workflow.

There is deliberately no HUMAN_DECISION_RECORDED event. A human decision is PR 004 evidence of kind human-decision, arriving through EVIDENCE_ADMITTED. EvidenceFreshness carries no verdict field, so this layer records that a human decided and is structurally unable to learn what they decided. PR 003's gate plus the human remain the only authority boundary.

Evaluation precedence is fixed and never varies, so rejection reasons are deterministic: state readable → event readable → kind recognised → not CLOSED → payload slot → status posture → upstream outcome → payload fields → binding → identity/replay → capacity → apply.

Ratified architecture decisions D1–D7

Ref Decision Implementation
D1 One bounded WorkflowState aggregate containing tracked invocations the join lives in the domain, so duplicate-id and unknown-invocation checks are possible at all
D2 Record evidence and review admissions, findingCount removed AdmittedReview is a stable pointer; review.findings is never read — no text, severity, classification, or count reaches state
D3 One logical workflow; boundCommitSha changes only via HEAD_OBSERVED; monotonic revision is the admission key boundCommitSha is assigned in exactly one function
D4 AWAITING_HUMAN_DECISION is a real status; work-initiating events refused while open; fact-recording still admitted an in-flight report is never lost
D5 No parent/supersession/DAG/causal fields; revision containment only no such field exists anywhere in src/
D6 Third self-contained hardened reader set; parity guard extended; PR 005/006 untouched no shared untrusted-input.ts; reader-parity.test.ts is now three-way
D7 Capacity exhaustion rejects with CAPACITY_EXCEEDED, returning the identical prior state orchestration history is never truncated

A1 — a human gate clears on HEAD_OBSERVED

An applied HEAD_OBSERVED unconditionally sets status: OPEN and humanGateOpenedAtRevision: null alongside the rebind and revision + 1. No branch on prior status.

A human gate is commit-bound orchestration state, not authority. It is opened against the bound commit, so once that binding moves the gate is as stale as any other old-revision fact. Clearing removes no human authority: no approval is inferred, nothing is cancelled, no policy is applied, and a decision recorded against the superseded commit is subsequently refused EVIDENCE_NOT_CURRENT. A later PR may open a new gate at the new revision when its policy requires one.

Consequent pinned invariant: humanGateOpenedAtRevision is always null or exactly revision. Its only non-derivable content is whether a gate was open at closure, which is why it is retained on close. The relationship is enforced when a state is read back and asserted by a test, so the two values cannot disagree.

A2 — admittedAtCommitSha retained

AdmittedEvidence and AdmittedReview each carry admittedAtCommitSha alongside admittedAtRevision and admittedAtSequence. Past bound commits are not otherwise recoverable from the aggregate, so retaining it is what keeps a persisted state independently auditable without a companion history table that does not yet exist. Admissions keep their commit binding verbatim across later HEAD moves.

A3 — unsolicited reviews remain admissible

A correctly bound, valid ReviewResult is admitted even when its reviewId matches no tracked invocation — automated forge reviewers and human reviewers produce real reviews AgentBridge did not request, and refusing them would make those invisible to orchestration.

Admitting one does not: transition any invocation (only INVOCATION_REPORTED does that), imply it was requested, imply sufficiency, imply policy satisfaction, imply authority, or trigger repair. The state records nothing that distinguishes a requested review from an unsolicited one — asserted by byte-comparing the two admissions. Whether a requested, attributable, independent, or specific review is required for a given decision is a policy question owned by a later PR.

revision and sequence

The two remain distinct and are never collapsed; they answer different questions.

  • sequence starts at 0 and advances by exactly one on every applied transition, never on a rejection. It is the total ordering and the natural optimistic-concurrency token for a later persistence layer — the deliberate substitute for a clock, since this layer reads none.
  • revision starts at 0 and advances only on an applied HEAD_OBSERVED. It is the admission key.

Commit ordering is never inferred. A SHA is opaque: no parent check, no ancestry test, no "is this newer". A HEAD that returns to a previous commit still advances the revision, so evidence admitted earlier cannot resurrect — revision, not SHA alone, is the admission key, which is what defends against a hostile or buggy adapter replaying a HEAD. Retained earlier admissions remain true at their own revision and commit; they simply stop counting.

Claim / evidence separation

PR 006's ladder is unchanged and PR 007 adds no rung. There is no code path from INVOCATION_REPORTED into any admission list — asserted behaviourally (a reported-complete report carrying 64 claims whose claimedCommitSha equals the bound commit produces zero admissions) and structurally (a test slices the handler out of the source and asserts it never mentions the admission lists or their types). Reaching "remotely observed" still requires a new record built from an independent adapter observation, arriving as a separate EVIDENCE_ADMITTED event.

EVIDENCE_ADMITTED takes a PR 004 EvidenceFreshness, not an EvidenceRecord. Freshness is never re-derived; PR 004 already answered, and its result carries the target it was answered against. The only checks are that state is CURRENT, reason is BOUND_TO_CURRENT_HEAD, and both targetRepositoryId and targetHeadSha match this workflow's binding — so a caller cannot launder stale evidence by judging it against a convenient target. No change to PR 004 was required.

INVOCATION_REPORTED binds against the tracked invocation's commit, not the workflow's current one: a report arriving after HEAD moved is a true historical fact and is recorded, but it admits no evidence.

Provider, purpose, and reported-status neutrality

Legality never depends on providerId, agentId, purpose, or reportedStatus. They are recorded for audit and read by no branch — every reference is a presence check, a vocabulary shape check, or a store, never a comparison against a specific label.

A parametrized test runs all 128 combinations of eight provider labels (including system, root, admin, agentbridge-internal), four purposes, and four reported statuses, asserting the resulting states are identical once the three recorded label fields are normalized. reported-complete and reported-failed produce indistinguishable transitions; a repair invocation produces no field a review invocation lacks.

CAPACITY_EXCEEDED

Bound Value
MAX_IDENTIFIER_LENGTH 256 (must equal PR 005's and PR 006's; pinned by test)
MAX_TRACKED_INVOCATIONS 256
MAX_ADMITTED_EVIDENCE 1 024
MAX_ADMITTED_REVIEWS 256
MAX_REVISION / MAX_SEQUENCE 1 000 000

Exceeding a bound rejects the transition and returns the identical prior state. This is a deliberate third convention: PR 004 collapses an over-length evidence set to zero and PR 005/006 truncate and flag, but both operate on elements of a single hostile payload. A transition instead carries one discrete fact, so refusing it visibly at the call site is the only outcome that loses nothing — silently dropping orchestration history would be the dangerous result. A workflow that reaches a bound is an escalation signal for a later PR.

Identifiers reject; nothing here truncates. There is no truncated field on any PR 007 type because this layer stores no prose.

Security and adversarial invariants

  • Trust boundary: WorkflowBinding, observedCommitSha, atCommitSha, and closureReason are trusted; AgentInvocation is trusted for binding and inert as authority; PR 004/005/006 results are pre-normalized but re-validated as hostile — trusting the type is not trusting the value.
  • HEAD is supplied, never inferred, mirroring PR 004's EvidenceTarget. No agent-controlled payload has a field through which it could be set.
  • Every field is read exactly once into a local, so an unstable getter cannot validate one value and store another. Properties are read own-only, so a __proto__ payload supplies nothing.
  • All identifier comparison is exact and case-sensitive with no trimming: a commit differing by case or padding does not match, which fails closed.
  • Absent and unreadable are kept apart. A present-but-unreadable pull request (oversized, blank, non-string, throwing getter) rejects rather than being treated as absent — treating it as absent would skip the comparison and silently discard the exact binding. (This was a fail-open defect found during the pre-commit audit and repaired, with four regression tests.)
  • Old-commit reviews and old-target verdicts can never advance the current revision; cross-repository and cross-pull-request replay reject.
  • A rejection returns the identical prior state reference — testable proof nothing was partially applied. Applied states are deeply frozen and JSON-round-trippable. A caller's extra properties are dropped, not carried forward.
  • A self-inconsistent state fails closed as WORKFLOW_UNREADABLE rather than being partially trusted.
  • Hostile input verified: non-objects, arrays, revoked Proxies, throwing and unstable getters, prototype pollution, poisoned Array/String/Set prototypes, replaced Object.freeze/Object.hasOwn, inherited numeric index setters. Nothing throws; everything fails closed.
  • No state, status, event, or rejection name implies merge, deploy, or write authority. Tests assert ~35 banned field names never appear as keys, no ALLOW/DENY/ESCALATE/AUTONOMOUS/CURRENT/STALE value reaches a serialized state, and the state exposes no boolean field at all.
  • -0 is rejected wherever a count is read: it compares equal to 0 but breaks byte identity across a JSON round trip.

Explicitly excluded — PR 008 and PR 009+

PR 008: retry counts, attempt limits, repair budgets, backoff, timeouts, deadlines, cancellation policy, loop termination, convergence detection, escalation policy, cost/token ceilings, sufficiency and quorum rules, and any next-action selection.

PR 009+: concrete Claude/OpenAI/Gemini/CodeRabbit adapters, real transport, GitHub mutations, artifact existence verification, integration detection, Evidence Store persistence, live external-service adapters, and end-to-end bridge integration.

Also absent: polling, queues, schedulers, async, concurrency control, provider/reviewer routing (roles stay configuration resolved before an invocation is constructed), merge-readiness policy, approval logic, human-approval UI, dashboards, identifier generation, commit ancestry inference, and any public projection API — there is deliberately no legalEventKinds(), because a public enumeration of what is permitted is one refactor away from being read as advice.

Verification

Check Result
npm run verify (typecheck + lint + test + build) PASS
Tests 1119 / 1119 passed, 14 files
New PR 007 tests 393 (182 behavioural + 208 invariant + 3 parity)
Pre-existing tests 726, all passing, none weakened or removed
npm audit 0 vulnerabilities
git diff --check clean
Dependency changes nonepackage.json, package-lock.json, tsconfig, eslint, vitest, and CI are untouched

Reviewed HEAD: 7cfbfc95c6da4e65fc10a710d29ff929630760fe.
Architecture record: docs/architecture/007-autoflow-state-machine.md.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added the Autoflow workflow state machine for tracking workflow and invocation lifecycles.
    • Added support for requests, reports, reviews, evidence, approvals, updates, human gates, HEAD observations, and closure events.
    • Added validation, replay protection, capacity limits, binding checks, deterministic rejection, and immutable state updates.
    • Exposed workflow models, event types, and transition results through the public domain API.
  • Documentation

    • Added architecture documentation covering workflow states, transitions, safeguards, and boundaries.
  • Tests

    • Added comprehensive coverage for lifecycle behavior, invalid inputs, immutability, determinism, and hostile data.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

PR 007 adds the Autoflow immutable workflow state machine. It defines commit-bound state and events, pure transitions, bounded readers, public exports, architecture rules, hostile-input handling, and lifecycle and invariant tests.

Changes

Autoflow workflow state machine

Layer / File(s) Summary
Workflow contracts and public surface
docs/architecture/007-autoflow-state-machine.md, src/domain/workflow.ts, src/domain/index.ts, tests/domain/reader-parity.test.ts, README.md
Defines workflow vocabularies, state and event types, bounded readers, transition results, public exports, architecture constraints, and reader-parity coverage.
State validation and workflow opening
src/domain/workflow-transitions.ts, tests/domain/workflow-fixtures.ts, tests/domain/workflow-invariants.test.ts, tests/domain/workflow-transitions.test.ts
Validates untrusted bindings, states, records, and payloads. Builds revision-zero states and deeply frozen snapshots.
Event admission and state transitions
src/domain/workflow-transitions.ts
Applies invocation, review, evidence, HEAD, human-gate, and closure events. Enforces bindings, replay protection, capacities, revisions, sequences, and rejection behavior.
Invariant and lifecycle validation
tests/domain/workflow-invariants.test.ts, tests/domain/workflow-transitions.test.ts, tests/domain/workflow-fixtures.ts
Covers lifecycle transitions, hostile inputs, immutability, determinism, serialization, vocabulary limits, reader parity, and end-to-end replay.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant openWorkflow
  participant applyWorkflowEvent
  participant WorkflowState
  Caller->>openWorkflow: WorkflowBinding
  openWorkflow-->>Caller: revision-zero WorkflowState
  Caller->>applyWorkflowEvent: WorkflowEvent and WorkflowState
  applyWorkflowEvent->>WorkflowState: validate bindings and invariants
  applyWorkflowEvent-->>Caller: applied or rejected TransitionResult
Loading

Poem

I’m a rabbit with a frozen state,
I bind each commit and check each gate.
Events hop in; bad ones stay,
Good ones leave a new array.
No clock, no carrot, no surprise.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 78.95% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the Autoflow state machine and its commit-bound, pure orchestration behavior.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pr-007/autoflow-state-machine

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7cfbfc95c6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1138 to +1142
if (readOwnProperty(verdictRecord, 'targetRepositoryId') !== snapshot.repositoryId) {
append(notCurrent, 'verdict.targetRepositoryId');
}
if (readOwnProperty(verdictRecord, 'targetHeadSha') !== snapshot.boundCommitSha) {
append(notCurrent, 'verdict.targetHeadSha');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Validate the evidence's own repository and commit

When a corrupted or caller-forged EvidenceFreshness claims CURRENT and supplies matching target fields but retains a different repositoryId or commitSha, these checks admit it as current because the evidence's own binding fields are never compared. This lets stale or cross-repository evidence enter the current revision, and a forged human-decision can additionally clear an open human gate; validate the verdict's repositoryId and commitSha against the workflow binding as well.

Useful? React with 👍 / 👎.

Comment on lines +470 to +476
const invocations: TrackedInvocation[] = [];
for (let index = 0; index < invocationCandidates.length; index += 1) {
const tracked = readTrackedInvocation(invocationCandidates[index], revision, sequence);
if (tracked === null) {
return null;
}
append(invocations, tracked);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject duplicate invocation IDs while reading state

When a deserialized or cast WorkflowState contains two tracked entries with the same invocationId, this snapshot loop accepts both even though invocation identity is workflow-wide. Reporting that ID then updates only the first entry found by indexOfInvocation; subsequent reports are rejected as already reported while the duplicate remains permanently REQUESTED, making behavior depend on array order. Detect duplicate IDs here and reject the aggregate as WORKFLOW_UNREADABLE.

Useful? React with 👍 / 👎.

Comment on lines +1263 to +1268
const atCommitSha = readExactIdentifier(readOwnProperty(eventRecord, 'atCommitSha'));
if (atCommitSha === null) {
return rejected(original, TRANSITION_REJECTION.EVENT_PAYLOAD_INVALID, [
'event.atCommitSha',
]);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Check the open-gate posture before payload fields

When HUMAN_GATE_OPENED is submitted while the workflow is already awaiting a human and atCommitSha is malformed, this early validation returns EVENT_PAYLOAD_INVALID instead of HUMAN_GATE_ALREADY_OPEN. That contradicts the documented fixed precedence of status posture before deep payload fields and can send callers down the wrong recovery path; perform the already-open check before reading and validating atCommitSha.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@codex please implement the three CURRENT findings on PR #9 only.

Current reviewed HEAD:
7cfbfc9

Treat every finding on an older SHA as stale after HEAD changes.

Repair exactly:

P1 — Evidence own-binding validation
A pre-normalized/forged EvidenceFreshness must not be admitted merely because its target fields say CURRENT for this workflow.
Validate the evidence/verdict's own repository and commit binding against the workflow binding as required by the frozen PR 007 trust boundary.
A forged human-decision must never clear a human gate when its own evidence binding is stale/cross-repository.
P2 — Duplicate invocation IDs in deserialized state
While reading/validating a supplied WorkflowState, duplicate tracked invocationIds must make the aggregate WORKFLOW_UNREADABLE.
Do not allow order-dependent report behavior.
P2 — HUMAN_GATE_OPENED rejection precedence
Preserve the documented fixed precedence.
If the workflow is already AWAITING_HUMAN_DECISION, return HUMAN_GATE_ALREADY_OPEN before deep-reading/validating atCommitSha.

Preserve D1–D7 and A1–A3 exactly.

Add focused adversarial regression tests for all three repairs.

Do not redesign PR 007.
Do not add PR 008/009 functionality.
Do not refactor unrelated hardened readers.
Do not make unrelated cleanup.

Run the complete verification suite and npm audit.

Before commit/push, audit the repair for:

evidence own-binding exactness;
old-SHA/cross-repository evidence rejection;
human-decision gate safety;
duplicate invocation identity rejection;
deterministic rejection precedence;
provider/purpose/reported-status neutrality;
claim/evidence separation;
revision/sequence semantics;
no PR 008/009 scope creep;
all existing tests green.

If direct update of the current PR branch is unavailable, create a child repair PR targeting pr-007/autoflow-state-machine, not main.

Do not merge anything.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Testing

  • npm run verify — typecheck, lint, all 1,125 tests, and build passed.
  • git diff --check
  • ⚠️ npm audit — attempted as requested, but the npm registry audit endpoint returned HTTP 403 in this environment.

View task →

@LogicDuke

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@LogicDuke

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@LogicDuke

Copy link
Copy Markdown
Owner Author

@codex please perform a fresh FINAL REVIEW of PR #9 at the CURRENT HEAD.

Determine the exact current HEAD SHA yourself before reviewing.

Treat every finding and review attached to an older SHA as stale.

REVIEW ONLY.

Do not implement repairs.
Do not modify files.
Do not merge.
Do not begin PR 008.

Review the complete PR 007 implementation, including the Codex repair and subsequent documentation-only precedence clarification.

Re-check the frozen PR 007 architecture D1–D7 and A1–A3, with particular attention to:

evidence own repository/commit binding;
forged/stale/cross-repository evidence rejection;
human-decision gate safety;
duplicate invocation identity rejection;
HUMAN_GATE_OPENED rejection precedence;
claim/evidence separation;
revision vs sequence semantics;
stale review/evidence handling;
capacity fail-closed behavior;
provider/purpose/reportedStatus neutrality;
hostile-runtime totality;
deterministic rejection behavior;
no PR 008/009 scope creep;
no regression to PR 001–006;
agreement between executable behavior, tests, and architecture documentation.

Report findings only against the exact current HEAD.

If no findings remain, explicitly state that the current HEAD is clean and ready for merge.

Do not merge anything.

@LogicDuke

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@LogicDuke

Copy link
Copy Markdown
Owner Author

@coderabbitai help

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

CodeRabbit commands

These commands are invoked using PR/Issue comments.

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai rate limit to show your current review rate limit status.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai emit path instructions to emit generated path-specific review instructions for this repository.
  • @coderabbitai resolve to resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai generate configuration to create a PR that adds the current resolved configuration as .coderabbit.yaml (or show it if already present).
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit configuration file (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, documentation and community

  • Visit our status page to check the current availability of CodeRabbit.
  • Visit our documentation site for detailed information on how to use CodeRabbit.
  • Join our Discord community to connect with other users and get help from the community.
  • Follow us on X/Twitter for updates and announcements.

@LogicDuke

Copy link
Copy Markdown
Owner Author

@coderabbitai full review
@coderabbitai rate limit

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Your plan includes PR reviews subject to rate limits. More reviews will be available in 53 minutes.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a22eb520b9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

for (let index = 0; index < length; index += 1) {
let element: unknown;
try {
element = elements[index];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject inherited entries in workflow lists

When a deserialized state contains a sparse invocations, evidence, or reviews array and Array.prototype has a corresponding numeric property, elements[index] reads that inherited value and materializes it as an own workflow record. A prototype-planted invocation can therefore be reported, and prototype-planted evidence or reviews become durable after the next applied event, contradicting the hostile-runtime and own-input guarantees. Require every index below length to be an own property, or reject the state as WORKFLOW_UNREADABLE.

Useful? React with 👍 / 👎.


const evidence: AdmittedEvidence[] = [];
for (let index = 0; index < evidenceCandidates.length; index += 1) {
const admitted = readAdmittedEvidence(evidenceCandidates[index], revision, sequence);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Verify current admissions against the bound commit

When a deserialized or cast state contains an evidence admission whose admittedAtRevision equals the workflow's current revision but whose admittedAtCommitSha differs from boundCommitSha, this reader accepts and preserves it. Because currentness is keyed by revision, the stale or cross-commit record then appears to be a current admission and can also cause a legitimate admission with the same ID to be rejected as a duplicate; the equivalent problem exists for reviews. Reject such an internally impossible aggregate as WORKFLOW_UNREADABLE.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@codex please implement the two CURRENT P1 findings on PR #9 only.

Determine the exact current PR HEAD yourself before modifying anything.

Treat all findings on older SHAs as stale after HEAD changes.

Repair exactly these two findings:

P1 — Reject inherited entries in workflow lists

When reading/deserializing invocations, evidence, and reviews, every numeric index from 0 through length - 1 must be an own property of the array.

A sparse slot that resolves through Array.prototype[index] must never be accepted or materialized into workflow state.

Fail closed as WORKFLOW_UNREADABLE.

Apply this consistently to all security-relevant workflow arrays.

Add adversarial tests proving prototype-planted numeric properties cannot:

fabricate a tracked invocation;
fabricate admitted evidence;
fabricate an admitted review;
become durable after a subsequent legitimate transition.

P1 — Verify current admissions against bound commit

When reading a supplied/deserialized WorkflowState, enforce the internal invariant:

if an AdmittedEvidence or AdmittedReview has
admittedAtRevision === workflow.revision,
then
admittedAtCommitSha === workflow.boundCommitSha
must also hold exactly.

Otherwise reject the aggregate as WORKFLOW_UNREADABLE.

Do not alter the historical-admission rule: admissions from older revisions may legitimately retain their earlier admittedAtCommitSha.

Add adversarial tests for both evidence and reviews proving:

current-revision mismatched commit → WORKFLOW_UNREADABLE;
current-revision matching commit → accepted;
older-revision historical admission with prior commit → still accepted;
an impossible forged current admission cannot block or shadow a later legitimate admission by ID.

Preserve D1–D7 and A1–A3 exactly.

Do not redesign PR 007.
Do not add PR 008/009 functionality.
Do not refactor unrelated hardened readers.
Do not make unrelated cleanup.

Run the complete verification suite, npm audit, and git diff --check.

Before commit/push, audit specifically for:

inherited numeric properties;
sparse arrays;
Array.prototype poisoning;
own-property enforcement;
current revision/commit consistency;
historical admission validity;
stale evidence/review rejection;
revision vs sequence semantics;
claim/evidence separation;
provider/purpose/reportedStatus neutrality;
no PR 008/009 scope creep.

If direct update of the existing PR branch is unavailable, create a child repair PR targeting pr-007/autoflow-state-machine, not main.

Do not merge anything.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

  • Confirmed the exact starting PR HEAD was a22eb520b96615f832e17ba5eb68d3ed326ee416 before making changes.
  • Hardened all three workflow-state list readers—invocations, evidence, and reviews—by requiring every numeric array index to be an own property. Sparse arrays resolving entries through Array.prototype now fail closed as WORKFLOW_UNREADABLE. src/domain/workflow-transitions.tsL183-L218
  • Added current-admission consistency checks for both evidence and reviews: an admission at the workflow’s current revision must have an admittedAtCommitSha exactly matching boundCommitSha, while historical admissions remain valid. src/domain/workflow-transitions.tsL495-L519
  • Added adversarial tests proving prototype-planted invocations, evidence, and reviews cannot be materialized or become durable through a later transition. tests/domain/workflow-invariants.test.tsL515-L547
  • Added evidence and review regression coverage for mismatched current commits, matching current commits, valid historical admissions, and forged same-ID admissions failing as WORKFLOW_UNREADABLE before they can shadow legitimate admissions. tests/domain/workflow-invariants.test.tsL549-L597
  • Committed the repair as 067b69a (Harden workflow state admission readers) and prepared child-repair PR metadata targeting pr-007/autoflow-state-machine.

Testing

  • npm test -- --run tests/domain/workflow-invariants.test.ts — 213 focused invariant tests passed.
  • npm run verify — typecheck, lint, all 1,130 tests, and build passed.
  • git diff --check
  • ⚠️ npm audit — the npm registry audit endpoint returned HTTP 403 in this environment.

View task →

LogicDuke and others added 2 commits August 11, 2026 14:19
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@LogicDuke

Copy link
Copy Markdown
Owner Author

@codex please perform a fresh FINAL REVIEW of PR #9 at the CURRENT HEAD.

Determine the exact current HEAD SHA yourself before reviewing.

Treat every finding and review attached to an older SHA as stale.

REVIEW ONLY.

Do not modify code.
Do not implement repairs.
Do not commit.
Do not merge.
Do not begin PR 008.

Review the complete current PR 007 implementation, including all previous repairs.

Focus especially on:

current-revision invocation commit binding;
current-revision evidence commit binding;
current-revision review commit binding;
historical revision/commit integrity;
sparse/inherited workflow array entries;
prototype poisoning;
duplicate invocation IDs;
evidence own repository/commit binding;
forged human-decision evidence;
HUMAN_GATE_OPENED precedence;
claim/evidence separation;
revision vs sequence semantics;
provider/purpose/reportedStatus neutrality;
CAPACITY_EXCEEDED fail-closed behavior;
hostile-runtime totality;
immutable/JSON-round-trippable state;
D1–D7;
A1–A3;
no PR 008/009 scope creep;
no regression to PR 001–006.

Review tests critically. Do not assume 1136 green tests prove correctness.

Report findings only against the exact current HEAD.

If no material findings remain, explicitly state that the CURRENT HEAD is clean and ready for merge.

@LogicDuke

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@LogicDuke

Copy link
Copy Markdown
Owner Author

@coderabbitai rate limit

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Your plan includes PR reviews subject to rate limits. More reviews will be available in 1 minute.

@LogicDuke

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f570ec67e2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1233 to +1234
const gateOpen = snapshot.status === WORKFLOW_STATUS.AWAITING_HUMAN_DECISION;
const clearing = gateOpen && rawKind === EVIDENCE_KIND.HUMAN_DECISION;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Validate complete CURRENT verdict shape before clearing gate

When a cast or deserialized verdict has matching state/reason/bindings and kind: 'human-decision' but an impossible PR 004 shape such as source: null or nonempty invalidFields, it is still admitted here and clears an open human gate. Since every genuine CURRENT EvidenceFreshness has a valid source and empty invalid fields, validate those invariants before treating the kind as a recorded human decision.

Useful? React with 👍 / 👎.

Comment on lines +490 to +494
if (
tracked === null ||
(tracked.requestedAtRevision === revision &&
tracked.targetCommitSha !== boundCommitSha)
) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep historical revision bindings internally consistent

After HEAD_OBSERVED advances the revision, these current-only checks accept historical invocations, evidence, and reviews stamped with the same prior revision but different commit SHAs. No possible workflow history can bind one revision to multiple commits, yet the next applied event freezes and preserves that corrupted audit history; validate a single consistent commit binding for every represented revision, including historical ones.

Useful? React with 👍 / 👎.

Comment on lines +421 to +422
const revision = readCount(readOwnProperty(record, 'revision'), WORKFLOW_BOUNDS.MAX_REVISION);
const sequence = readCount(readOwnProperty(record, 'sequence'), WORKFLOW_BOUNDS.MAX_SEQUENCE);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject revisions that exceed the transition sequence

A deserialized state with revision: 2 and sequence: 0 passes this snapshot and can be closed into a state with revision 2 and sequence 1. Because every revision increment is itself an applied transition and every applied transition increments sequence, revision > sequence is unreachable and corrupts both the documented total ordering and later capacity behavior; reject such aggregates as WORKFLOW_UNREADABLE.

Useful? React with 👍 / 👎.

Comment on lines +505 to +515
const evidence: AdmittedEvidence[] = [];
for (let index = 0; index < evidenceCandidates.length; index += 1) {
const admitted = readAdmittedEvidence(evidenceCandidates[index], revision, sequence);
if (
admitted === null ||
(admitted.admittedAtRevision === revision &&
admitted.admittedAtCommitSha !== boundCommitSha)
) {
return null;
}
append(evidence, admitted);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject duplicate admissions already present in state

When a cast or deserialized state already contains two evidence entries with the same (evidenceId, admittedAtRevision)—or the equivalent review pair—this reader accepts both even though the transition handlers prohibit that replay identity. A later event then makes the duplicates durable and lets them consume capacity or be double-counted by downstream policy; detect duplicate admission keys while snapshotting both collections.

Useful? React with 👍 / 👎.

@LogicDuke

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@LogicDuke

Copy link
Copy Markdown
Owner Author

@coderabbitai rate limit

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (9)
tests/domain/workflow-invariants.test.ts (1)

2085-2108: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Make the purity scan token-aware instead of substring-based.

toContain matches any substring. Date matches updateDate or Validate. process matches processed. crypto matches cryptographic. A future rename inside workflow.ts or workflow-transitions.ts can fail this test without introducing impurity. stripComments also removes /* */ and // sequences that appear inside string literals or regular expressions, so the scanned text can differ from real code.

Match whole tokens with word boundaries, and keep the member-access patterns explicit.

♻️ Proposed refactor
-      for (const forbidden of [
-        'Date',
-        'Math.random',
-        'process',
-        'globalThis',
-        'require(',
-        'node:',
-        'async ',
-        'await ',
-        'Promise',
-        'setTimeout',
-        'crypto',
-        'randomUUID',
-      ]) {
-        expect(source).not.toContain(forbidden);
-      }
+      for (const forbidden of [
+        /\bDate\b/,
+        /\bMath\s*\.\s*random\b/,
+        /\bprocess\b/,
+        /\bglobalThis\b/,
+        /\brequire\s*\(/,
+        /\bfrom\s+['"]node:/,
+        /\basync\b/,
+        /\bawait\b/,
+        /\bPromise\b/,
+        /\bsetTimeout\b/,
+        /\bcrypto\b/,
+        /\brandomUUID\b/,
+      ]) {
+        expect(source).not.toMatch(forbidden);
+      }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/domain/workflow-invariants.test.ts` around lines 2085 - 2108, Update
the purity scan in the test case reading workflow.ts and workflow-transitions.ts
to use token-aware regular-expression matching instead of substring toContain
checks. Match standalone identifiers with word boundaries, while keeping
member-access patterns such as Math.random explicit, and avoid stripping
comment-like sequences inside string literals or regular expressions when
preparing source for scanning.
tests/domain/workflow-fixtures.ts (1)

48-85: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider sharing oversized and label with the existing invocation fixtures.

oversized and label are byte-identical to oversized and label in tests/domain/invocation-fixtures.ts (lines 33-35 and 43-69). The production readers are duplicated on purpose for boundary independence, and tests/domain/reader-parity.test.ts pins that duplication. Test helpers carry no such requirement, so a shared helper module would remove the copy without weakening any guarantee.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/domain/workflow-fixtures.ts` around lines 48 - 85, The oversized and
label helpers are duplicated across workflow and invocation fixtures. Move them
into a shared test-helper module, update both fixture modules to import and
reuse those shared exports, and remove their local implementations while leaving
the production reader duplication unchanged.
src/domain/workflow-transitions.ts (3)

978-1002: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Fail closed instead of skipping an undefined element.

appendTo and replaceAt skip an element when element === undefined. The guard exists to satisfy noUncheckedIndexedAccess, and the input lists come from a validated snapshot, so undefined is not reachable today. The behaviour still contradicts the rule this module states on line 382: the layer refuses to shorten orchestration history silently. If a future change makes a hole reachable, the copy shortens without any signal.

Copy the element unconditionally, so the type guard cannot become a silent drop.

♻️ Proposed change
 function appendTo<T>(list: readonly T[], value: T): readonly T[] {
   const next: T[] = [];
   for (let index = 0; index < list.length; index += 1) {
-    const element = list[index];
-    if (element !== undefined) {
-      append(next, element);
-    }
+    append(next, list[index] as T);
   }
   append(next, value);
   return objectFreeze(next);
 }
 
 function replaceAt<T>(list: readonly T[], target: number, value: T): readonly T[] {
   const next: T[] = [];
   for (let index = 0; index < list.length; index += 1) {
-    const element = list[index];
     if (index === target) {
       append(next, value);
-    } else if (element !== undefined) {
-      append(next, element);
+    } else {
+      append(next, list[index] as T);
     }
   }
   return objectFreeze(next);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/domain/workflow-transitions.ts` around lines 978 - 1002, Update appendTo
and replaceAt to preserve every list position by copying each indexed element
unconditionally instead of skipping undefined values; retain replaceAt’s
substitution behavior for the target index and keep the existing immutable
return behavior.

693-957: 🚀 Performance & Scalability | 🔵 Trivial

Note the per-call validation cost as history grows.

snapshotWorkflow runs full re-validation on every applyWorkflowEvent call, including calls that are rejected immediately afterwards, such as the WORKFLOW_CLOSED check on line 1156. Several passes are quadratic in the retained history size:

  • the evidence duplicate scan at lines 808-817 is O(n²) with n up to MAX_ADMITTED_EVIDENCE (1 024);
  • claimSequence rescans seenSequences for each stamp, and that array can reach ~1 792 entries;
  • revisionBandsOrdered is O(d²) over distinct revisions.

At the declared bounds this is a few million primitive comparisons per call, and a caller that replays an N-event log pays it N times. The bounds keep it finite and deterministic, so this is not a correctness or availability defect. If a persistence layer later replays long logs, consider validating once per loaded state and passing the validated snapshot forward, rather than re-validating per event.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/domain/workflow-transitions.ts` around lines 693 - 957, Reduce repeated
validation in the applyWorkflowEvent flow by validating a loaded workflow state
once and passing the validated snapshot forward instead of invoking
snapshotWorkflow on every event, including immediately rejected events such as
WORKFLOW_CLOSED. Preserve all existing snapshotWorkflow invariants and ensure
event application still receives validated state.

1066-1078: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Drive the invalid-field order from WORKFLOW_BINDING_FIELD_ORDER, or correct its doc comment.

src/domain/workflow.ts lines 357-362 state that invalid-field reporting walks WORKFLOW_BINDING_FIELD_ORDER, so the order of invalidFields is deterministic and stable. This function instead hardcodes the order in four separate append calls, and WORKFLOW_BINDING_FIELD_ORDER is not read by any production path. The constant is still exported publicly from src/domain/index.ts line 168.

The order currently matches, and tests/domain/workflow-transitions.test.ts lines 174-188 pin it, so there is no live defect. Either read the order from the constant here, or update the constant's doc comment so it does not describe behaviour the code does not implement.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/domain/workflow-transitions.ts` around lines 1066 - 1078, Update the
invalid-field reporting around the invalid array construction to either iterate
according to WORKFLOW_BINDING_FIELD_ORDER, or revise that constant’s public
documentation in workflow.ts to stop claiming it governs invalidFields ordering.
Prefer using the exported constant so the implementation and documented
deterministic order remain aligned, while preserving the existing validation
conditions and field names.
tests/domain/reader-parity.test.ts (1)

405-414: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Align the test body with its name.

The test is named pins PR 007 as having no field that truncates, but the first two assertions exercise ingestInvocationReport, which is the PR 006 boundary. The local variable workflow holds an InvocationReportResult, not a workflow value. The only PR 007 assertion on line 414 repeats the check already made on lines 296 and 400.

Consider asserting the PR 007 no-truncation property directly, for example that an oversized identifier never appears as a prefix in a openWorkflow or applyWorkflowEvent result.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/domain/reader-parity.test.ts` around lines 405 - 414, The test body
does not directly verify PR 007’s no-truncation behavior. In the test named
“pins PR 007 as having no field that truncates,” remove the unrelated
ingestInvocationReport assertions and replace the duplicated
workflowReadExactIdentifier check with an assertion using openWorkflow or
applyWorkflowEvent that confirms an oversized identifier is never retained as a
truncated prefix.
tests/domain/workflow-transitions.test.ts (3)

878-886: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Make the finding-count assertion less brittle.

expect(serialized).not.toContain('25') guards against a stored finding count. The state also serializes sequence, revision, and every identifier, so any future fixture value containing the substring "25" makes this test fail for an unrelated reason. The current fixtures avoid it, so the test passes today.

Consider asserting on the absent key instead of the raw count value, for example checking that no review admission object has more than the four expected keys.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/domain/workflow-transitions.test.ts` around lines 878 - 886, Replace
the brittle serialized-value check in the workflow transition test with a
structural assertion that review admission objects do not contain the stored
finding-count field, such as verifying they contain only the four expected keys.
Keep the existing checks for review count and excluded sentinel fields
unchanged.

192-219: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Drop the unused fourth tuple column.

The cases tuple type declares a fourth element string | null, every row sets it to null, and the it.each callback at line 213 destructures only three parameters. The column carries no assertion. It looks like a copy of awaitingCases, where the third element is used.

♻️ Proposed change
-  const cases: readonly (readonly [string, () => WorkflowEvent, string, string | null])[] = [
+  const cases: readonly (readonly [string, () => WorkflowEvent, string])[] = [
     [
       'INVOCATION_REQUESTED',
       () => requestInvocation(buildInvocation({ invocationId: INVOCATION_B })),
       'OPEN',
-      null,
     ],
-    ['INVOCATION_REPORTED', () => reportInvocation(), 'OPEN', null],
-    ['REVIEW_ADMITTED', () => admitReview(), 'OPEN', null],
-    ['EVIDENCE_ADMITTED', () => admitEvidence(), 'OPEN', null],
+    ['INVOCATION_REPORTED', () => reportInvocation(), 'OPEN'],
+    ['REVIEW_ADMITTED', () => admitReview(), 'OPEN'],
+    ['EVIDENCE_ADMITTED', () => admitEvidence(), 'OPEN'],
     [
       'EVIDENCE_ADMITTED human-decision',
       () => admitEvidence(buildHumanDecisionVerdict()),
       'OPEN',
-      null,
     ],
-    ['HEAD_OBSERVED different', () => observeHead(SHA_B), 'OPEN', null],
-    ['HUMAN_GATE_OPENED', () => openHumanGate(), 'AWAITING_HUMAN_DECISION', null],
-    ['CLOSE_REQUESTED', () => closeWorkflow(), 'CLOSED', null],
+    ['HEAD_OBSERVED different', () => observeHead(SHA_B), 'OPEN'],
+    ['HUMAN_GATE_OPENED', () => openHumanGate(), 'AWAITING_HUMAN_DECISION'],
+    ['CLOSE_REQUESTED', () => closeWorkflow(), 'CLOSED'],
   ];
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/domain/workflow-transitions.test.ts` around lines 192 - 219, Remove the
unused fourth element from every tuple in the OPEN transition `cases` array and
update its tuple type to contain only the name, event builder, and expected
status. Keep the existing three-parameter `it.each` callback and assertions
unchanged.

1233-1233: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Also assert the round-tripped state is accepted back by the state machine.

toEqual compares values and ignores frozen-ness. readList in src/domain/workflow-transitions.ts rejects any unfrozen list at lines 420-428, so a state rebuilt with JSON.parse has three unfrozen lists and is rejected as WORKFLOW_UNREADABLE. The source comment at lines 418-419 states that a caller must re-freeze the three collections.

The current assertion proves value equality only. Adding the acceptance check would pin the documented caller obligation, and would keep the obligation visible if readList changes.

💚 Proposed addition
     expect(JSON.parse(JSON.stringify(state))).toEqual(state);
+
+    // A JSON round trip loses frozen-ness, and `readList` requires it. The
+    // documented caller obligation is to re-freeze the three collections.
+    const parsed = JSON.parse(JSON.stringify(state)) as WorkflowState;
+    expect(applyWorkflowEvent(parsed, observeHead(SHA_C)).rejection).toBe(
+      'WORKFLOW_UNREADABLE',
+    );
+    const refrozen = Object.freeze({
+      ...parsed,
+      invocations: Object.freeze(parsed.invocations),
+      evidence: Object.freeze(parsed.evidence),
+      reviews: Object.freeze(parsed.reviews),
+    });
+    expect(applyWorkflowEvent(refrozen, observeHead(SHA_C)).rejection).toBe('WORKFLOW_CLOSED');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/domain/workflow-transitions.test.ts` at line 1233, Extend the
round-trip assertion near the existing JSON equality check to pass the parsed
state back through the state-machine validation path and assert it is accepted.
Use the existing workflow transition/read API and confirm the result is not
WORKFLOW_UNREADABLE, while preserving the current value-equality assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/domain/workflow-transitions.test.ts`:
- Around line 357-367: Update
tests/domain/workflow-transitions.test.ts#L357-L367 in the duplicate
invocation-id test to freeze the invocations array so validation reaches the
duplicate-id scan. Also update
tests/domain/workflow-transitions.test.ts#L1233-L1233 after the JSON round-trip
assertion to verify the raw parsed state is rejected with WORKFLOW_UNREADABLE
and that an equivalent re-frozen copy is accepted, covering the readList caller
requirement.

---

Nitpick comments:
In `@src/domain/workflow-transitions.ts`:
- Around line 978-1002: Update appendTo and replaceAt to preserve every list
position by copying each indexed element unconditionally instead of skipping
undefined values; retain replaceAt’s substitution behavior for the target index
and keep the existing immutable return behavior.
- Around line 693-957: Reduce repeated validation in the applyWorkflowEvent flow
by validating a loaded workflow state once and passing the validated snapshot
forward instead of invoking snapshotWorkflow on every event, including
immediately rejected events such as WORKFLOW_CLOSED. Preserve all existing
snapshotWorkflow invariants and ensure event application still receives
validated state.
- Around line 1066-1078: Update the invalid-field reporting around the invalid
array construction to either iterate according to WORKFLOW_BINDING_FIELD_ORDER,
or revise that constant’s public documentation in workflow.ts to stop claiming
it governs invalidFields ordering. Prefer using the exported constant so the
implementation and documented deterministic order remain aligned, while
preserving the existing validation conditions and field names.

In `@tests/domain/reader-parity.test.ts`:
- Around line 405-414: The test body does not directly verify PR 007’s
no-truncation behavior. In the test named “pins PR 007 as having no field that
truncates,” remove the unrelated ingestInvocationReport assertions and replace
the duplicated workflowReadExactIdentifier check with an assertion using
openWorkflow or applyWorkflowEvent that confirms an oversized identifier is
never retained as a truncated prefix.

In `@tests/domain/workflow-fixtures.ts`:
- Around line 48-85: The oversized and label helpers are duplicated across
workflow and invocation fixtures. Move them into a shared test-helper module,
update both fixture modules to import and reuse those shared exports, and remove
their local implementations while leaving the production reader duplication
unchanged.

In `@tests/domain/workflow-invariants.test.ts`:
- Around line 2085-2108: Update the purity scan in the test case reading
workflow.ts and workflow-transitions.ts to use token-aware regular-expression
matching instead of substring toContain checks. Match standalone identifiers
with word boundaries, while keeping member-access patterns such as Math.random
explicit, and avoid stripping comment-like sequences inside string literals or
regular expressions when preparing source for scanning.

In `@tests/domain/workflow-transitions.test.ts`:
- Around line 878-886: Replace the brittle serialized-value check in the
workflow transition test with a structural assertion that review admission
objects do not contain the stored finding-count field, such as verifying they
contain only the four expected keys. Keep the existing checks for review count
and excluded sentinel fields unchanged.
- Around line 192-219: Remove the unused fourth element from every tuple in the
OPEN transition `cases` array and update its tuple type to contain only the
name, event builder, and expected status. Keep the existing three-parameter
`it.each` callback and assertions unchanged.
- Line 1233: Extend the round-trip assertion near the existing JSON equality
check to pass the parsed state back through the state-machine validation path
and assert it is accepted. Use the existing workflow transition/read API and
confirm the result is not WORKFLOW_UNREADABLE, while preserving the current
value-equality assertion.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c8b4db1b-12cf-4a27-b270-024139834436

📥 Commits

Reviewing files that changed from the base of the PR and between 4635b0c and 8800805.

📒 Files selected for processing (9)
  • README.md
  • docs/architecture/007-autoflow-state-machine.md
  • src/domain/index.ts
  • src/domain/workflow-transitions.ts
  • src/domain/workflow.ts
  • tests/domain/reader-parity.test.ts
  • tests/domain/workflow-fixtures.ts
  • tests/domain/workflow-invariants.test.ts
  • tests/domain/workflow-transitions.test.ts

Comment thread tests/domain/workflow-transitions.test.ts
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@LogicDuke

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a03c0eff3b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/domain/workflow-transitions.ts Outdated
) {
return false;
}
if (earlier < later && earlierHigh >= laterLow) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reserve sequence slots for intervening HEAD revisions

When retained stamps move forward by multiple revisions, this comparison checks only their relative order and not whether enough sequence slots exist for the intervening HEAD_OBSERVED transitions. For example, a request at revision 0/sequence 1 followed by its report at revision 2/sequence 3 passes every current check, although the two required HEAD transitions must occupy sequences 2 and 3, leaving the report no possible slot; the next event then preserves an unreachable audit history. Require the sequence gap between ordered stamps to exceed the revision delta, and apply the equivalent constraint between the final stamp and the aggregate counters.

Useful? React with 👍 / 👎.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@LogicDuke

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ad14bbc815

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +783 to +786
rawStatus === WORKFLOW_STATUS.AWAITING_HUMAN_DECISION &&
humanGateOpenedAtRevision === null
) {
return null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reserve sequence slots for status transitions

When a deserialized state has status: AWAITING_HUMAN_DECISION, humanGateOpenedAtRevision: 0, and sequence: 0, these checks accept it even though opening the gate must already have consumed sequence 1; a subsequent HEAD_OBSERVED is then applied using that same first slot. The equivalent omission admits a CLOSED state whose sequence has no room for its closing transition. Require the aggregate chronology to reserve an unstamped sequence slot for the current gate-opening or closing transition.

Useful? React with 👍 / 👎.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@LogicDuke

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 977d854ba7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +974 to +978
if (
(rawStatus === WORKFLOW_STATUS.AWAITING_HUMAN_DECISION ||
rawStatus === WORKFLOW_STATUS.CLOSED) &&
sequence <= seenSequences.length
) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Account for HEAD slots when reserving the gate transition

Fresh evidence on this HEAD is that the new count check still accepts an unreachable awaiting-human state after a HEAD change: with revision: 1, sequence: 2, and one current-revision evidence admission stamped at sequence 2, seenSequences.length is only 1, so this condition passes even though sequence 1 is necessarily occupied by HEAD_OBSERVED and no slot remains for HUMAN_GATE_OPENED. A subsequent fact-recording event can then apply and preserve the corrupt history; verify that an unstamped slot exists at the current revision after the last required HEAD transition rather than comparing against the number of retained stamps.

Useful? React with 👍 / 👎.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@LogicDuke

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4aae023c20

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +963 to +965
// Revision never decreases, so ordering every stamped record by sequence must
// yield non-decreasing revisions.
if (!revisionBandsOrdered(spanRevisions, spanLowest, spanHighest, revision, sequence)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject unexplained gaps in the sequence counter

An OPEN state with revision: 0, sequence: 1, and all three collections empty passes these checks, after which a HEAD_OBSERVED event is applied at sequence 2. No transition can produce the supplied state: at revision 0 every sequence-advancing event either leaves a retained stamp, changes the status to awaiting-human, or closes the workflow. Because records are never deleted, accepting unexplained sequence gaps corrupts the documented total order and optimistic-concurrency token; validate an upper accounting bound for unstamped transitions as well as the existing lower chronology bounds.

Useful? React with 👍 / 👎.

Comment on lines +981 to +984
(rawStatus === WORKFLOW_STATUS.AWAITING_HUMAN_DECISION ||
rawStatus === WORKFLOW_STATUS.CLOSED) &&
sequence <= revision + seenSequences.length
) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reserve the gate slot after reaching the current revision

Fresh evidence on this reviewed tree is that an awaiting-human state with revision: 1, sequence: 3, and one historical admission at revision 0/sequence 2 passes this count and even accepts a subsequent HEAD_OBSERVED. The historical admission must be followed by the revision-advancing HEAD at sequence 3, leaving no slot in revision 1 for the HUMAN_GATE_OPENED transition that the status requires. Comparing only the total number of stamps therefore still admits an unreachable aggregate; ensure the open-gate transition has a slot after the final transition needed to reach the current revision.

Useful? React with 👍 / 👎.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@LogicDuke

Copy link
Copy Markdown
Owner Author

@codex Please perform one fresh final review of PR #9 against the CURRENT HEAD only:

808e8e9

STRICT REVIEW BOUNDARY:

  • Review exactly this HEAD and the PR PR 007 — Autoflow State Machine: commit-bound, pure orchestration state #9 diff against main.
  • Treat every finding, comment, analysis, or review attached to any older SHA as STALE.
  • Do not carry forward an older finding unless you independently reproduce it against this exact HEAD.
  • Do not speculate about defects that are not reproducible from the current tree.
  • Do not propose architectural redesigns or broader improvements outside the PR PR 007 — Autoflow State Machine: commit-bound, pure orchestration state #9 scope.
  • Do not review hypothetical PR 008/009 work.
  • Preserve the existing AgentBridge V1 architecture and invariants.
  • Pay particular attention to the bounded chronology repairs and their interaction with the existing A1 clearing/reopening semantics.
  • Verify that legitimate HEAD-clearing and human-decision-clearing paths remain valid.
  • Verify that the chronology guards reject only states that are actually unreachable under the existing workflow semantics.
  • Verify that no persisted event log/history reconstruction, generalized chronology model, new public API, or unrelated behavior has been introduced.
  • Report only concrete, actionable defects that exist on this exact HEAD.

This is a FINAL REVIEW GATE, not an implementation request.

Do not modify code.

If you find an issue, provide the exact current-HEAD counterexample and identify the affected current lines.

If no actionable defects exist on this exact HEAD, state that the current PR is ready to merge.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 808e8e9f36

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/domain/workflow-transitions.ts Outdated
// Only an open gate at revision >= 1 is constrained. A gate that was already
// cleared — by a HEAD advance or by a human decision — leaves the workflow
// `OPEN`, and those histories are deliberately left alone.
if (rawStatus === WORKFLOW_STATUS.AWAITING_HUMAN_DECISION && revision > 0) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Place the closing slot after the final HEAD

When a frozen state represents HUMAN_GATE_OPENED at sequence 1, a human-decision admission at revision 0/sequence 2, and HEAD_OBSERVED reaching revision 1/sequence 3, changing it to status: CLOSED, closureReason: CALLER_CLOSED, and a cleared gate is accepted as readable and returns WORKFLOW_CLOSED. This state is unreachable because the terminal CLOSE_REQUESTED transition would require sequence 4; the placement guard only covers AWAITING_HUMAN_DECISION, so apply an analogous final-slot constraint to CLOSED states.

Useful? React with 👍 / 👎.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@LogicDuke

Copy link
Copy Markdown
Owner Author

@codex Please perform a fresh final review of PR #9 against the CURRENT HEAD only:

c18569b

Important review rules:

  • Review the complete PR as it exists at this exact HEAD.
  • Treat every finding, comment, or review attached to any older SHA as stale unless independently reproducible against this exact HEAD.
  • Do not rely on conclusions from previous reviews.
  • Re-evaluate the current implementation from the repository state at this HEAD.
  • Focus on correctness, state-machine invariants, chronology/accounting integrity, fail-closed behavior, and regressions.
  • Verify the recent CLOSED final-slot repair as part of the complete review.
  • Do not propose architectural expansion outside PR 007 unless required to identify a concrete correctness defect in the current implementation.

Please report only findings that are reproducible against:

c18569b

If there are no actionable findings on this exact HEAD, explicitly report that the review is clean.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c18569b4fb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1010 to +1014
if (bandRevision < revision && bandHighest > latestEarlier) {
latestEarlier = bandHighest;
}
}
if (sequence < latestEarlier + 2) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reserve the closing slot after every retained stamp

For a CLOSED state, CLOSE_REQUESTED must be the final transition because no subsequent event is legal, but this loop considers only stamps from earlier revisions. On the current HEAD, a revision-0 closed state with sequence: 2 and evidence stamped at sequence 2 is accepted as readable and returns WORKFLOW_CLOSED; the evidence consumed the alleged final slot, so the required close transition would actually need sequence 3. Require every retained sequence stamp, including current-revision stamps, to be strictly less than a closed aggregate's sequence.

Useful? React with 👍 / 👎.

Comment on lines +980 to +984
if (
(rawStatus === WORKFLOW_STATUS.AWAITING_HUMAN_DECISION ||
rawStatus === WORKFLOW_STATUS.CLOSED) &&
sequence <= revision + seenSequences.length
) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Account for both gate and close slots when the gate is retained

When a closed snapshot retains humanGateOpenedAtRevision, its history necessarily contains both an unstamped HUMAN_GATE_OPENED and a later unstamped CLOSE_REQUESTED, but this lower bound reserves only one status-producing slot. For example, a revision-0 state with empty collections, sequence: 1, status: CLOSED, and humanGateOpenedAtRevision: 0 is accepted and returns WORKFLOW_CLOSED, although opening the gate consumes sequence 1 and closing requires sequence 2. Reserve the additional gate slot whenever a closed state retains an open-gate revision.

Useful? React with 👍 / 👎.

Comment thread src/domain/workflow-transitions.ts Outdated
Comment on lines +1024 to +1028
if (
revision === 0 &&
rawStatus === WORKFLOW_STATUS.OPEN &&
seenSequences.length === 0 &&
sequence > 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject unexplained sequence gaps when records are retained

The current HEAD still applies the upper accounting check only when there are no retained stamps. Fresh reproduction: an OPEN revision-0 state with one ci-result admission at sequence 1 but aggregate sequence: 2 is accepted and can apply HEAD_OBSERVED, even though no transition can explain slot 2—there was no HEAD, closure is terminal, and a gate would leave the status awaiting because this evidence kind cannot clear it. Extend the upper accounting bound to histories with retained records, allowing only gate slots that a retained human decision or HEAD could actually have cleared.

Useful? React with 👍 / 👎.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@LogicDuke

Copy link
Copy Markdown
Owner Author

@codex Please perform the FINAL convergence review of PR #9 at the exact current HEAD:

a01f54d

Review the current HEAD only. Treat findings attached to every older SHA as stale.

This is a final correctness review of the frozen PR 007 scope.

Please report only concrete, reproducible defects present on this exact HEAD that violate the existing PR 007 architecture, invariants, or documented semantics.

Do not propose:

  • architectural expansion;
  • persisted history or a new ledger;
  • new public APIs;
  • stricter semantics not already required by PR 007;
  • speculative hardening;
  • refactoring or cleanup;
  • follow-up feature work.

Pay particular attention to the chronology/state-machine repairs and their interaction with existing D1–D7 and A1–A3 invariants.

If you identify a defect, provide a minimal reproducible state/event sequence demonstrating the violation on this exact HEAD.

If no such defect remains, explicitly state that no actionable current-HEAD findings were found.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a01f54d58e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/domain/workflow-transitions.ts Outdated
Comment on lines +1050 to +1054
// Deliberately bounded to revision 0 and `OPEN`. Once a HEAD advance is in
// play it clears a gate while leaving nothing behind, and no upper bound is
// claimed there. With no stamps at all this reduces to the untouched
// workflow: only sequence 0 is reachable.
if (revision === 0 && rawStatus === WORKFLOW_STATUS.OPEN) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Extend sequence upper bounds beyond revision zero

Fresh evidence on this exact HEAD: start with openWorkflow, then supply a frozen OPEN snapshot with empty frozen collections, revision: 1, and sequence: 3; applyWorkflowEvent(state, { kind: 'HEAD_OBSERVED', observedCommitSha: 'ccc' }) returns APPLIED at revision 2/sequence 4. This snapshot is unreachable: the sole HEAD needed to reach revision 1 can consume only slot 1, while neither slots 2 nor 3 can be explained—opening a gate would leave the status awaiting unless a retained human-decision stamp or another revision-advancing HEAD cleared it. Limiting the upper accounting check to revision 0 therefore still admits and perpetuates corrupt chronology.

Useful? React with 👍 / 👎.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@LogicDuke

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d6a73afcb5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/domain/workflow-transitions.ts Outdated
Comment on lines +1065 to +1066
const gatesOpened = revision + humanDecisions;
if (sequence > seenSequences.length + revision + gatesOpened) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Only count decisions that can clear an earlier gate

Fresh evidence after the latest upper-bound fix: an OPEN, revision-0 snapshot with one human-decision admission stamped at sequence 1 and aggregate sequence 2 still passes this formula. That decision cannot have cleared a gate because no earlier sequence slot exists to open one, while using slot 2 to open a gate would leave the workflow awaiting; nevertheless humanDecisions licenses that unexplained slot and the next event preserves an unreachable history. Count a decision as a possible gate clear only when chronology leaves room for an earlier gate-opening transition.

Useful? React with 👍 / 👎.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@LogicDuke

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7587ad91f8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// stamps at all to the untouched workflow, where only sequence 0 is
// reachable. Deliberately confined to `OPEN`: a retained gate or closure
// posture is accounted for by the lower bounds above.
if (rawStatus === WORKFLOW_STATUS.OPEN) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply sequence upper bounds to non-open states

When a deserialized AWAITING_HUMAN_DECISION state at revision 0 has empty frozen collections and sequence: 2, it passes the lower status-slot check and skips this upper bound; however, only the gate-opening transition can explain slot 1, leaving slot 2 unreachable. Checked against applyWorkflowEvent: a subsequent HEAD_OBSERVED is accepted and preserves the corrupt chronology at sequence 3. Extend the upper accounting to awaiting and closed states while reserving their required status-transition slots.

Useful? React with 👍 / 👎.

Comment on lines +1075 to +1076
if (admitted.admittedAtSequence - 1 > stampsBelow) {
clearingDecisions += 1;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Match each clearing decision to a distinct gate slot

Fresh evidence after this eligibility fix is an OPEN, revision-0 snapshot with human-decision admissions at sequences 2 and 3 and aggregate sequence 4: both decisions count because each sees the same unstamped slot 1, so the state is accepted and can apply HEAD_OBSERVED. In any real history, the first decision clears the gate opened at slot 1, the second clears nothing, and a gate at slot 4 would leave the workflow awaiting; count decisions only when each can be paired with its own earlier, still-uncleared gate slot.

Useful? React with 👍 / 👎.

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