Skip to content

feat(projection): per-target change classification on check and plan (#327 S4) - #347

Merged
Ancienttwo merged 16 commits into
mainfrom
claude/issue-327-s4-change-classification
Oct 14, 2026
Merged

Ancienttwo merged 16 commits into
mainfrom
claude/issue-327-s4-change-classification

Conversation

@Ancienttwo

@Ancienttwo Ancienttwo commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

Refs #327 (slice S4, finding 2: stamp noise). Does not close the issue on its own.

Feature flag: projection-change-classification-v1 is in ARCHCTX_FEATURES, the capabilities fixture and the schema (commit 656e6f4, added after #337 merged; archctx capabilities --json lists it). A consumer detects changes[] through that flag.

What changes

projection run check and plan results add a per-target changes[]:

{targetId, path, affectedCapabilityIds, stampChanged, outputChanged, semanticChanged, proofChanged, plannedAction, classification, reasonCodes}

Classification order. Each target gets the first class that applies:

  1. blocked
  2. proof-change, when the proof change is known
  3. baseline-missing
  4. semantic-change
  5. output-change
  6. stamp-only
  7. unchanged

When a target is stamp-only. All of these must hold:

  • the stamp changed;
  • the output bytes are unchanged;
  • the semantic and proof comparisons are both known and both unchanged;
  • the target is not blocked.

A missing baseline or fingerprint is null or baseline-missing. It is never reported as unchanged.

What each target will write. plannedAction (none|create|update|delete) comes from the same byte decision apply uses (projectionWriteFileResults). There is no second rule. Under planned or planned-partial, the targets with a planned action are exactly the documents apply writes. The manifest is never a changes[] entry; its write is the one files[] entry no change names.

Which targets are listed. changes[] covers current targets and committed-manifest targets, so additions and orphan deletes are included. An orphan that needs review is blocked with no action.

Where blocked comes from. It is derived from S7 isolation, review orphans and adoption/ownership rejections. The validator checks that every coverage.blockedTargets entry is blocked with the same reason codes. A rejected adoption target is blocked/none while files[] still lists it as update, because only an approved adoption writes it. That is the one allowed mismatch.

Global documents (index, decisions index, changelog, diagrams, relation summaries) depend on the whole model. They are never stamp-only, and a source-only edit never touches them. Any model semantic change marks them semantic-change, so they may over-report.

Unchanged: staleNodes, projection freshness (it does not classify), apply semantics and receipts.

Docs: a runbook section on telling stamp-only drift from real changes, and ADR-0047 §7.

Verification

  • typecheck
  • contracts + projection-engine + changeset-engine: 671 pass
  • local-runtime: 907 pass
  • surfaces + scripts: 581 pass
  • tests/: 4 pass
  • packaged-cli-smoke: OK
  • package-boundary-audit: passed
  • After the follow-up fixes: typecheck plus contracts and both classification suites, 379 pass

Real run on a scratch clone with a CodeGraph index:

  • A comment-only source edit gives stamp-only / none for that capability's module doc, and files holds only the manifest.
  • Crossing a scale bucket gives output-change / update, and files holds the manifest plus that doc.
  • On the branch tree, docs drift is ok, check is noop, and all 7 changes are unchanged.

The new tests failed on afeb0c3 before the implementation. They cover:

  • partial apply
  • proof-only change
  • semantic-only change
  • missing baseline and missing row
  • add and delete
  • marker-only on LF and CRLF
  • orphan
  • precedence
  • the planned-action/write invariant

… and plan results

ProjectionTargetChangeV1 and ProjectionResultV2.changes: one entry per
target path with stamp, output, semantic and proof comparisons (null when
unknown), the planned file action and a classification derived by one
precedence rule (blocked > baseline-missing > proof-change >
semantic-change > output-change > stamp-only > unchanged). The validators
bind plannedAction to files[], blocked targets to coverage, and keep
changes off apply results and receipts. The result schema and its
embedded readback copy carry the same targetChange definition.

Refs #327
…esults

check and plan results carry changes[]: one entry per target path over
the current layout, the committed manifest rows and the orphans. The
comparisons reuse what the render holds (row stamps and output digests,
per-capability semantic and flow-proof fingerprints, selector-evidence
digests); plannedAction is the result's own files[] action, so the
targets with a planned action are exactly the documents apply writes or
deletes. Phase 1 carried-forward targets, review orphans and adoption or
ownership rejections are blocked. A source-only edit is stamp-only with
only the manifest written; a missing baseline is baseline-missing, never
unchanged. Status, files, apply, receipts and freshness are unchanged.

Refs #327
Runbook: the changes[] fields, the classification table and precedence,
plannedAction semantics, the baseline-missing state, and how a consumer
ignores stamp-only drift. ADR-0047 section 7 records the decision.

Refs #327
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-14T05:20:34.150077Z f882d81 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: f882d810f6

ℹ️ 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 packages/contracts/src/projection.ts
Comment thread packages/contracts/src/projection.ts
A global target (index, decisions index, changelog, diagrams, relation
summaries) renders the whole model, including nodes no capability owns.
Comparing only capability fingerprints left such targets unchanged when a
module or system node outside every capability moved. They now also compare
the committed semanticBaseline.modelDigest with the current model digest and
report semantic-change with the new model-digest-changed reason code. Module
documents keep their per-capability comparison, and a source-only edit never
moves the model digest.

Refs #327
…ry file action in changes

A path rejected for adoption or ownership repair keeps plannedAction none
and stays blocked, but its files[] update proves the on-disk bytes differ
from the proposed ones. outputChanged and file-bytes-changed now follow the
underlying files[] action instead of the blocked write, and the contract
requires outputChanged whenever the path's files[] entry changes a file.

The contract also checks the reverse binding: every non-manifest files[]
create, update or delete, rejected paths included, has a change of its
path, and no change names the projection manifest. The manifest path
constant moves next to the result contract that now reads it.

Refs #327
…e action

The protocol promises changes on every check and plan result, but the JSON
schema cannot condition on the request mode, so the mode validator now
reports a check or plan result without changes. Every producer path already
builds its result through projectionProtocolResult, which classifies for
check and plan and runs this validator before returning.

The change invariants also refuse a change that names the projection
manifest. Tests cover a missing change for a non-manifest create, update or
delete, the mode check per status, and end to end a model change outside
every capability and an adoption-required edited index whose blocked change
keeps file-bytes-changed. The runbook and ADR-0047 describe both bindings.

Refs #327
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