Skip to content

feat(ingest): attach a same-type, same-key candidate to the existing concept instead of forking it - #1283

Merged
jasonssdev merged 16 commits into
mainfrom
spec/1268-attach-at-ingest
Oct 2, 2026
Merged

jasonssdev merged 16 commits into
mainfrom
spec/1268-attach-at-ingest

Conversation

@jasonssdev

Copy link
Copy Markdown
Owner

Summary

Deliverable 1 of "The Quiet Engine" arc (#1268): attach, don't fork, at ingest. When an extracted candidate matches an existing concept on the same OKF type and the same normalized title key, ingest revises that concept instead of writing <slug>-N. This removes the -N duplicate families at the source rather than queueing them for review.

This PR also carries the OpenSpec proposals for deliverables 1 and 2 (openspec/changes/attach-at-ingest/ and openspec/changes/retire-superseded-sources/) with the owner's decisions recorded. Only attach-at-ingest is implemented here; retire-superseded-sources (#1259, #1263) follows separately.

Behavior

  • Staging looks up (type, normalize_key(title)) among non-deprecated concepts before the slug-collision path. A match becomes an attach plan; no match keeps today's behavior.
  • An attach unions provenance, appends the new evidence as a section (deterministic, no model call), takes the high-water sensitivity, adds one ## Related bullet and bumps version by 1. Id, title, description and type never change. It is the first place the engine bumps version.
  • Excluded: Event and Person (homonyms; adjudicate --apply-same: a cross-source SAME verdict merged two meetings held a week apart, and the survivor is chosen alphabetically #776, adjudicate: identical Event titles are recurring-meeting occurrences, not one entity (the judgment half of #776) #796) keep the fork path, through one constant. A deprecated same-key match is never an attach target. A same-source match stays the byte-untouched no-op.
  • attach_at_ingest config key, default on for every path including the daemon's inbox watch, with a kill switch (false).
  • The write is a guarded atomic rewrite; an interrupted attach completes without a second version bump. Cataloging writes an **Attach** log entry and no second index bullet.
  • The CLI shows an attach in the preview, the summary and the batch line (e.g. ingested (revised 1 existing object)).
  • ADR-0045 (Proposed) records the decision; docs/cli.md updated.

Reuse, not duplication

Spec deviation from the proposal (applied to the delta spec): the existing-concept lookup is a lazy, memoised find/read pair read after extraction, not a precomputed mapping, to keep the read close to the commit phase.

Existing tests changed: six slug-collision tests in test_ingest_behaviour.py now use Person (an attach-excluded type) so they still pin the fork path; the batch_auto characterization golden sets attach_at_ingest: false to keep pinning the fork stream; test_candidate_edges_e2e's fake keys extraction on source text; examples/good-life-demo/openkos.yaml gains the template comment.

Size: one PR by the maintainer's choice; about 2,100 lines of implementation and tests plus about 1,900 lines of OpenSpec proposals.

Related issue

Refs #1268
Refs #1259

Type of change

  • feat — new feature
  • fix — bug fix
  • docs — documentation only
  • refactor — no behavior change
  • test — tests only
  • chore / ci — tooling, build, or CI
  • Breaking change

Ingest now revises an existing same-type, same-key concept instead of writing <slug>-N by default; set attach_at_ingest: false to keep the previous behavior.

How was this tested?

  • Strict TDD with observed RED for each unit (e.g. 17 failures for build_attached_document, 9 for the config key, 5 for the service, 3 for CLI output). The merge goldens passed against the unrefactored code first, so the refactor has a byte-exact oracle.
  • Watched-edit end-to-end test: editing a watched source revises the existing concepts instead of producing -N copies.
  • Mutation checks, each turning named tests red: Person exclusion (5), Event exclusion (3), deprecated-target exclusion (2), same-type (1), same-source no-op (4), kill switch (2), sensitivity high-water mark (2), version + 1 (7), guarded-target registration (1).
  • After rebasing onto current main: ruff check, ruff format --check, mypy . (524 files), evals/run_self_tests.py, and pytest --cov (9582 passed, 2 skipped, 96.47%) pass.

Checklist

  • My commits follow Conventional Commits.
  • I added or updated tests for the change.
  • I updated docs where behavior, interfaces, or the knowledge model changed.
  • Lint, format, type check, and tests pass locally (ruff, mypy, pytest).
  • Output remains OKF-conformant and derived stores stay reconstructible from the bundle + sources.
  • The change is consistent with the project's guiding principles (local-first, provenance, freshness, human-in-the-loop).

Two OpenSpec changes for deliverables 1 and 2 of the "Quiet Engine" arc:
ingest attaches a same-type, same-key candidate to the existing concept
instead of writing a -N copy (Event and Person excluded), and deprecation
follows the provenance of a superseded Source with a clean forget path.

Refs #1268
Refs #1259
Refs #1263
Merge output is unchanged byte for byte (goldens).

Refs #1268
…/-N identity rule

keyed_documents and canonical_family_member are shared by the HIGH
Identity tier, merge and (next) ingest attach.

Refs #1268
Event and Person are excluded; a deprecated concept is never a target; a
source that already owns a match stays the byte-untouched no-op.

Refs #1268
Attach targets join the drift guard instead of the create-only set, are
read after extraction, and the commit message names revisions separately.
Existing slug-collision tests move to Person, an attach-excluded type.

Refs #1268
…y and batch

The attach key also lands in the example workspace config, and two existing
tests that relied on a same-title fork keep it through the kill switch or
distinct titles.

Refs #1268
@jasonssdev jasonssdev added the size:exception PR exceeds the 400-line review budget with maintainer-accepted exception label Oct 2, 2026
@jasonssdev
jasonssdev merged commit a59d206 into main Oct 2, 2026
9 checks passed
@jasonssdev
jasonssdev deleted the spec/1268-attach-at-ingest branch October 2, 2026 22:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:exception PR exceeds the 400-line review budget with maintainer-accepted exception

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant