Add verified reference lookup and immutable consumer receipts

Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a1045b-933d-7aa1-b2db-104557487a79
This commit is contained in:
tegwick 2026-10-04 09:02:35 +02:00
parent c7f87b791b
commit bd85fcd179
15 changed files with 583 additions and 11 deletions

View file

@ -36,3 +36,7 @@ reference-check:
reference-test:
PYTHONDONTWRITEBYTECODE=1 "$(BENCH_PY)" -m pytest -q -p no:cacheprovider tests/test_reference_preview.py
.PHONY: consumption-test
consumption-test:
PYTHONDONTWRITEBYTECODE=1 "$(BENCH_PY)" -m pytest -q -p no:cacheprovider tests/test_reference_consume.py

View file

@ -1,6 +1,6 @@
# SCOPE
Reviewed: 2026-10-04 · Capability audit at `07f1f37`.
Reviewed: 2026-10-04 · Audited at `07f1f37`; updated with WP-0010 delivery.
## What this repository delivers
@ -20,6 +20,8 @@ Research fixtures exercise bounded mechanisms; they are not implementation recip
| --- | --- | --- |
| Human entry and two core sequences | Follow shared-record editing or offline-work decisions, alternatives, failure traces and implementation questions | [START.md](START.md), [transactional](sequences/TransactionalApplication.md), [offline](sequences/LocalFirstApplication.md); synthetic VC-01/02 guidance |
| Frozen 0.1 candidate reference | Read 17 pinned draft hypotheses (8 core, 9 extensions), 9 conditional proposed relations, classifications and evidence gaps | [Release registry](reference/releases.json), [index](reference/0.1/index.json); not an admitted canonical graph |
| Local reference consumption | Filter the verified preview by explicit candidate ID, improving forces and responsibilities; emit corpus/packaging citations, force costs and conditional relations; clarify or return no match | [reference_consume.py](scripts/reference_consume.py); deterministic metadata filtering, not natural-language retrieval, applicability or compatibility proof |
| Consumer event preparation | Produce a schema-v1 event with verified release pins, explicit provenance, unknown or evidenced producer outcomes, immutable file and exact collector-index digest | [Consumer instructions](feedback/ConsumerIntegration.md#local-lookup-and-closeout-helper); does not install hooks, enroll sources or independently verify outcome/lineage |
| Current research corpus | Inspect 18 developed candidate hypotheses with evidence profiles and retained source/runtime reviews | [Evolution report](research/evolution/EvolutionReport.md); the additional live candidate is outside frozen 0.1, no canonical admissions |
| Implementation mappings | Locate scoped mechanisms for transactional and offline recovery | [TransactionalRecovery](technologies/TransactionalRecovery.md), [OfflineRecovery](technologies/OfflineRecovery.md); no product supplies a certified complete configuration |
| Metadata and publication checks | Validate contract examples, candidate inventory/hashes, classification, evidence locators and immutable release artifacts | `make schema-test evolution-check classification-check reference-check`; checks establish structural traceability, not source truth |

View file

@ -60,6 +60,23 @@ intended to keep learning.
## Agent reference
For an explicit candidate, force or responsibility, use the local pinned-reference
helper:
```bash
python3 scripts/reference_consume.py --version 0.1 lookup --candidate CAND-0007
python3 scripts/reference_consume.py --version 0.1 lookup --force recoverability --responsibility mutation
```
Filters intersect; multiple candidate IDs form an allowlist. Results carry force
costs, evidence gaps, conditional relations and commit-specific contract/evidence
locators. A metadata match is a research lead, not an applicability or compatible
configuration proof. With no filters the helper requests context; valid filters with
no results produce `no-match`. Unknown vocabulary or unpublished IDs fail explicitly.
Read the complete pinned contracts before making the consumer's decision.
The [consumer receipt instructions](feedback/ConsumerIntegration.md#local-lookup-and-closeout-helper)
explain how to retain that decision and its unknown or later observed outcome.
Read [index.json](reference/0.1/index.json) for stable research identities, proposed
relations, classification, evidence and gaps. It is a candidate reference export, not
an admitted canonical graph. Read the linked contracts before making a recommendation;

View file

@ -17,6 +17,7 @@
| workplan | FEP-WP-0007 | finished | — | workplans/FEP-WP-0007-language-v01-preview.md |
| workplan | FEP-WP-0008 | blocked | — | workplans/FEP-WP-0008-use-driven-improvement.md |
| workplan | FEP-WP-0009 | backlog | — | workplans/FEP-WP-0009-m1-independent-validation.md |
| workplan | FEP-WP-0010 | finished | — | workplans/FEP-WP-0010-reference-consumption.md |
| task | FEP-WP-0001-T01 | done | — | workplans/FEP-WP-0001-statehub-bootstrap.md |
| task | FEP-WP-0001-T02 | done | — | workplans/FEP-WP-0001-statehub-bootstrap.md |
| task | FEP-WP-0001-T03 | done | — | workplans/FEP-WP-0001-statehub-bootstrap.md |
@ -46,3 +47,7 @@
| task | FEP-WP-0009-T01 | wait | — | workplans/FEP-WP-0009-m1-independent-validation.md |
| task | FEP-WP-0009-T02 | todo | — | workplans/FEP-WP-0009-m1-independent-validation.md |
| task | FEP-WP-0009-T03 | wait | — | workplans/FEP-WP-0009-m1-independent-validation.md |
| task | FEP-WP-0010-T01 | done | — | workplans/FEP-WP-0010-reference-consumption.md |
| task | FEP-WP-0010-T02 | done | — | workplans/FEP-WP-0010-reference-consumption.md |
| task | FEP-WP-0010-T03 | done | — | workplans/FEP-WP-0010-reference-consumption.md |
| task | FEP-WP-0010-T04 | done | — | workplans/FEP-WP-0010-reference-consumption.md |

View file

@ -0,0 +1,52 @@
{
"observed_at": "2026-10-04T07:00:38.801351+00:00",
"workplan": "FEP-WP-0010",
"kind": "disclosed-development-verification",
"assessment": "history/2026-10-04T084941+0200-scope-vs-intent.md",
"audit_revision": "07f1f373744986a22bc4f18710de94534f40343d",
"reference": {
"schema_version": 1,
"current": "0.1",
"releases": {
"0.1": {
"packaging_revision": "53bb65e7a6eb1a2fa933e5b41dfa5a0e7409d14d",
"manifest_sha256": "d7f60ac04817820b805265d1d747cfbb8d2269b7bd6244cd9d3e8d2fd44ef0b8"
}
}
},
"tests": {
"consumption": 31,
"schema": 2,
"bench_and_admission": 33,
"evolution": 22,
"reference_preview": 16,
"total": 104,
"skipped": 0
},
"checks": [
"synthetic bench plan; no provider execution",
"evolution consistency",
"classification consistency",
"frozen reference consistency",
"actual activity-core validator accepted generated synthetic event",
"reuse-surface capability schema and relations",
"changed Markdown local links",
"git diff --check"
],
"owner_contract": {
"repo": "activity-core",
"revision": "3dd68d118b3fb60e45926dcb238d4f1ab4d447ac",
"path": "src/activity_core/context_resolvers/frontend_patterns.py",
"sha256": "33aae2f3f0a17dfa90580c611de474064ca52b0aebf73939a9476a8560f46757"
},
"new_real_uses": 0,
"new_consumer_enrollments": 0,
"model_calls": 0,
"production_mutations": 0,
"limitations": [
"Metadata filtering does not establish applicability or compatibility.",
"Synthetic tests do not establish real value, independent human validation or held-out retrieval.",
"Producer preserves supplied lineage/outcome locators; collector and empirical review retain independent verification.",
"Hosted registration, actual closeout integration and linked consumer outcomes remain WP-0008-T01/T02."
]
}

View file

@ -12,6 +12,49 @@ implementation authority. A deployment repair or documentation-only inspection o
this pattern repository is not an external frontend use. Unsupported contexts should
produce an explicit no-match decision, not a forced pattern selection.
## Local lookup and closeout helper
The local helper is available before hosted federation enrollment. From the reference
checkout, `python3 scripts/reference_consume.py --version 0.1 lookup --candidate CAND-0007`
returns a verified candidate with exact corpus/packaging citation pins. Force and
responsibility filters narrow research leads; they do not make the consumer decision.
Read the linked contracts and their alternatives. Preserve abstention and unsupported
needs; do not turn a matching force into a complete architecture claim.
The [decision-input example](decision-input.example.json) is explicitly synthetic,
with dummy provenance. It is a format example, never an eligible real use. For an
actual eligible task, the consumer creates its own JSON with stable use/event IDs,
explicit `kind`, full consumer commit, task/producer/run references, constraints,
considered/selected IDs, action/reason/artifact reference and an observable outcome
expectation. Unknown is the default outcome; reported helpfulness requires observation
and evidence locators and still awaits independent verification. No private code,
credentials or transcript belongs in the input.
From that consumer's checkout, using the path to the reference tool:
```bash
python3 /path/to/frontend-patterns/scripts/reference_consume.py --version 0.1 receipt \
--input decision.json --output research/pattern-use/use-01/event-01.json
```
The helper validates input and the frozen release, fills its version/packaging/digest
pins, exclusively writes a new source-relative file, and prints the exact path/SHA-256
entry for the consumer's schema-v1 receipt index. JSON is valid YAML for activity-core's
existing collector. Without `--output`, it prints the event for inspection. It never
overwrites a receipt, enrolls a source or supplies collector-verified origin.
A later outcome uses the same `use_id`, a new `event_id` and the preceding file's
`previous_event_sha256`. Retain the earlier event/index entry; chronology and predecessor
integrity are checked by the existing collector. The helper preserves a supplied
digest but does not independently verify the event chain or outcome artifact.
Consumer owners still integrate invocation into their normal closeout hook, commit the
receipt/index and arrange authorized enrollment through WP-0008. This preparation does
not establish hosted discovery, automatic hook installation, real value or the loop's
delayed-artifact refresh. `make consumption-test` includes disclosed synthetic cases
and an integration check against the sibling activity-core validator when its Python
environment is present; release acceptance requires that check to run without a skip.
## Short planning and closeout instruction
> For eligible frontend architecture work, query

View file

@ -18,6 +18,10 @@ Consumer index (at most 100 entries across one collection):
```
Events follow [PatternUseReceipt.yaml](../templates/PatternUseReceipt.yaml).
The [local producer helper](ConsumerIntegration.md#local-lookup-and-closeout-helper)
prepares verified reference pins and a new immutable JSON/YAML event plus its index
entry from explicit consumer provenance. It does not install the closeout hook or
enroll a consumer. `decision-input.example.json` is a synthetic format fixture.
One stable `use_id` links immutable events; `previous_event_sha256` identifies a
follow-up. Identical events deduplicate; conflicting IDs, missing predecessors,
branches/cycles and malformed records are quarantined in the report. Synthetic

View file

@ -0,0 +1,28 @@
{
"use_id": "fixture-consumer:FEP-DEV-01:decision-01",
"event_id": "fixture-decision-01",
"recorded_at": "2026-10-04T06:49:41Z",
"kind": "synthetic",
"consumer": {
"repo": "fixture-consumer",
"revision": "0000000000000000000000000000000000000000",
"task_ref": "fixture:FEP-DEV-01",
"producer": "disclosed-development-fixture",
"run_ref": "fixture:no-external-use"
},
"context": {
"problem": "A shared-record edit timed out; decide whether creating another attempt is justified.",
"constraints": ["The original operation result is unknown.", "Replay identity and retention are not yet verified."],
"considered": ["CAND-0007"],
"selected": []
},
"decision": {
"action": "rejected",
"reason": "Do not claim safe replay before verifying the authority's lookup and retention contract.",
"artifact_ref": null
},
"outcome": {
"expectation": "A later authority-contract check establishes whether bounded replay is available.",
"next_observation": "Actual consumer would link its authority-contract check; fixture remains unknown."
}
}

View file

@ -84,5 +84,23 @@ validation and production autonomy remain separate evidence requirements.
## Verification and completion record
To be filled from implementation receipts before closeout. The capability inventory
must describe the delivered tool behavior and retain all empirical limitations.
FEP-WP-0010 was registered (UUID `c46b02ca-26a8-5313-b0f2-eb0385ff820c`) and its
finite local bridge implemented. The lookup returns explicit matches, needs-context or
no-match with verified pins, full force costs and conditional relations. The producer
validates explicit provenance/selection/outcome input, defaults to unknown, writes
exclusively and emits exact index bytes/digests. Caller classifications and assertions
remain caller claims; no consumer was enrolled and no real use was fabricated.
104 tests passed with zero skips, including actual activity-core validator acceptance;
synthetic planning, evolution/classification/frozen-publication checks passed.
[Verification record](../docs/evidence/2026-10-04-reference-consumption.json) pins the
owner contract revision/hash. No paid inference or production mutation occurred.
SCOPE, START, consumer instructions and capability guidance were reconciled to delivered
behavior. Existing WP-0008 T01/T02 received the integration/outcome residual in their
live task descriptions before this finite workplan was finished.
The highest-priority remaining gap is still real consumer decisions and inspectable
outcomes; the next is independent validation. The bridge reduces preparation friction,
but its tests do not establish either outcome. Broader applicability, autonomous
improvement and comparable learning retain the owners and gates listed above.
The resolved implementation decision is `6914093c-e540-4d31-a1ea-607021c5e185`.

View file

@ -49,14 +49,14 @@ relations:
related_to: []
evidence:
documentation: [START.md, reference/0.1/ReleaseReview.md, docs/ReuseAndImprovementLoop.md]
tests: [tests/test_reference_preview.py]
tests: [tests/test_reference_preview.py, tests/test_reference_consume.py]
consumer_feedback: []
bug_reports: []
incidents: []
consumer_guidance:
recommended_for: [frontend architectural decision notes, reviewing shared-edit and offline-recovery assumptions]
not_recommended_for: [claiming canonical maturity or production guarantees, treating a match score as applicability proof]
known_limitations: [all candidates are hypotheses, no independent human usefulness result, use feedback automation not deployed]
known_limitations: [all candidates are hypotheses, no independent human usefulness result, no enrolled consumers, admitted improvement and automatic publication remain open]
promotion_history: []
---
@ -72,5 +72,9 @@ boundaries. Their presence does not imply a complete sequence for those contexts
The [reuse and improvement contract](../../docs/ReuseAndImprovementLoop.md) defines
how consumer decisions and later outcomes will improve this language. Receipt collection
and unattended improvement require WP-0008 implementation; do not imply that recording
`reused` in registry telemetry already measures helpfulness.
and deterministic reporting are operational through activity-core; consumer closeout
integration, actual outcomes and admitted improvement remain WP-0008 acceptance work.
The [local lookup/receipt helper](../../feedback/ConsumerIntegration.md#local-lookup-and-closeout-helper)
provides verified pins and immutable producer files; it does not establish hosted
registration, consumer value or automatic improvement. Recording `reused` in registry
telemetry does not measure helpfulness.

View file

@ -0,0 +1,176 @@
#!/usr/bin/env python3
"""Filter a verified preview and prepare pinned consumer events; no inference."""
import argparse
from datetime import datetime, timezone
import hashlib
import json
import math
from pathlib import Path
import re
from reference_preview import ROOT, build, git_blob, release, require
from classification_coverage import section_values, RESPONSIBILITIES
HOST = 'https://forgejo.coulomb.social/coulomb/frontend-patterns'
SHA = re.compile(r'[0-9a-f]{40}')
DIGEST = re.compile(r'[0-9a-f]{64}')
IDENTITY = re.compile(r'[A-Za-z0-9][A-Za-z0-9:_.-]{0,159}')
def published(root=ROOT, version=None):
base, pin, manifest = release(root, version)
generated = build(root, version)
for name, content in generated.items():
require((root / base / name).read_text() == content, 'Published export changed: ' + name)
return json.loads(generated['index.json']), pin, manifest
def locator(path, revision):
return {'path': path, 'revision': revision,
'url': f'{HOST}/src/commit/{revision}/{path}'}
def lookup(index, pin, manifest, candidates=(), forces=(), responsibilities=(), root=ROOT):
known = {c['id'] for c in index['candidates']}
vocab = git_blob(root, manifest['corpus_revision'], 'canon/vocabulary.yaml').decode()
require(set(candidates) <= known, 'Unknown candidate in selected release')
require(set(forces) <= set(section_values(vocab, 'forces')), 'Unknown force')
require(set(responsibilities) <= set(RESPONSIBILITIES), 'Unknown responsibility')
matches = []
if candidates or forces or responsibilities:
for candidate in index['candidates']:
classification = candidate['classification']
if candidates and candidate['id'] not in candidates:
continue
if not set(forces) <= set(classification['forces']['improves']):
continue
if not set(responsibilities) <= set(classification['responsibilities']):
continue
record = dict(candidate)
record['contract'] = locator(candidate['path'], index['corpus_revision'])
record['evidence'] = [dict(e, locator=locator(e['ref'], pin['packaging_revision']))
for e in candidate['evidence']]
matches.append(record)
ids = {c['id'] for c in matches}
return {
'schema_version': 1,
'status': 'needs-context' if not (candidates or forces or responsibilities) else ('matches' if matches else 'no-match'),
'reference': {'version': index['version'], 'packaging_revision': pin['packaging_revision'],
'manifest_sha256': pin['manifest_sha256'], 'corpus_revision': index['corpus_revision']},
'filters': {'candidates': list(candidates), 'improving_forces': list(forces), 'responsibilities': list(responsibilities)},
'candidates': matches,
'relations': [e for e in index['relations'] if e['source'] in ids or e['target'] in ids],
'entrypoints': {k: locator(v, pin['packaging_revision']) for k, v in index['entrypoints'].items()},
'limits': ['Metadata filtering is not applicability, ranking or compatible-configuration proof.',
'Read full pinned contracts, force costs, alternatives and gaps before selecting.',
'Relations are conditional proposals; no inverse, transitive or automatic selection.',
'No match means no record satisfies these filters, not that no architecture exists.',
'This preview contains hypotheses; independent usefulness remains unverified.'],
}
def text_ok(value, limit=1000):
return isinstance(value, str) and 0 < len(value.strip()) <= limit
def receipt(source, index, pin, now=None):
require(isinstance(source, dict), 'Decision input must be an object')
fields = {'use_id', 'event_id', 'recorded_at', 'kind', 'consumer', 'context', 'decision', 'outcome'}
require(fields <= source.keys() <= fields | {'previous_event_sha256'}, 'Decision input fields mismatch')
event = json.loads(json.dumps(source))
event.update(schema_version=1, reference={
'version': index['version'], 'packaging_revision': pin['packaging_revision'],
'manifest_sha256': pin['manifest_sha256']})
for key in ('use_id', 'event_id'):
require(isinstance(event[key], str) and IDENTITY.fullmatch(event[key]), 'Invalid event identity')
require(event['kind'] in {'real', 'synthetic'}, 'Explicit real/synthetic kind required')
previous = event.setdefault('previous_event_sha256', None)
require(previous is None or isinstance(previous, str) and DIGEST.fullmatch(previous), 'Invalid predecessor digest')
require(isinstance(event['recorded_at'], str), 'Timestamp required')
stamp = datetime.fromisoformat(event['recorded_at'].replace('Z', '+00:00'))
require(stamp.tzinfo is not None and stamp <= (now or datetime.now(timezone.utc)), 'Invalid/future timestamp')
consumer = event['consumer']
require(isinstance(consumer, dict) and set(consumer) == {'repo', 'revision', 'task_ref', 'producer', 'run_ref'}, 'Consumer fields mismatch')
require(isinstance(consumer['repo'], str) and re.fullmatch(r'[a-z0-9][a-z0-9-]{0,79}', consumer['repo']), 'Invalid consumer repo')
require(isinstance(consumer['revision'], str) and SHA.fullmatch(consumer['revision']), 'Full consumer revision required')
require(all(text_ok(consumer[k]) for k in ('task_ref', 'producer', 'run_ref')), 'Missing consumer provenance')
context = event['context']
require(isinstance(context, dict) and set(context) == {'problem', 'constraints', 'considered', 'selected'}, 'Context fields mismatch')
require(text_ok(context['problem']), 'Problem required')
require(isinstance(context['constraints'], list) and all(text_ok(x, 500) for x in context['constraints']), 'Invalid constraints')
known = {c['id'] for c in index['candidates']}
for key in ('considered', 'selected'):
values = context[key]
require(isinstance(values, list) and all(isinstance(v, str) for v in values), 'Invalid candidate list')
require(len(values) == len(set(values)) and set(values) <= known, 'Unknown/duplicate candidate in release')
require(set(context['selected']) <= set(context['considered']), 'Unconsidered selection')
decision = event['decision']
require(isinstance(decision, dict) and set(decision) == {'action', 'reason', 'artifact_ref'}, 'Decision fields mismatch')
require(decision['action'] in {'applied', 'adapted', 'rejected', 'no-match'}, 'Invalid decision action')
require(bool(context['selected']) == (decision['action'] in {'applied', 'adapted'}), 'Action/selection mismatch')
require(text_ok(decision['reason']), 'Decision reason required')
require(decision['artifact_ref'] is None or text_ok(decision['artifact_ref']), 'Invalid artifact reference')
outcome = event['outcome']
defaults = {'value': 'unknown', 'basis': 'consumer-report', 'stage': 'decision',
'observation': None, 'evidence_refs': [], 'next_observation': None,
'measured_decision_minutes': None, 'measured_rework_minutes': None, 'comparison_ref': None}
require(isinstance(outcome, dict) and 'expectation' in outcome and set(outcome) <= set(defaults) | {'expectation'}, 'Outcome fields mismatch')
event['outcome'] = outcome = dict(defaults, **outcome)
require(text_ok(outcome['expectation']), 'Observable expectation required')
require(outcome['value'] in {'helpful', 'harmful', 'mixed', 'no-effect', 'unknown'}, 'Invalid outcome')
require(outcome['basis'] in {'consumer-report', 'model-assessment', 'observed-result'}, 'Invalid outcome basis')
require(outcome['stage'] in {'decision', 'implementation', 'test', 'operation'}, 'Invalid outcome stage')
refs = outcome['evidence_refs']
require(isinstance(refs, list) and len(refs) <= 10 and all(text_ok(v) for v in refs), 'Invalid outcome evidence')
require(outcome['observation'] is None or text_ok(outcome['observation']), 'Invalid observation')
require(outcome['value'] == 'unknown' or refs and text_ok(outcome['observation']), 'Observed outcome needs evidence')
for key in ('next_observation', 'comparison_ref'):
require(outcome[key] is None or text_ok(outcome[key]), 'Invalid outcome reference')
for key in ('measured_decision_minutes', 'measured_rework_minutes'):
value = outcome[key]
require(value is None or type(value) in (int, float) and math.isfinite(value) and value >= 0, 'Invalid measured effort')
return event
def write_receipt(event, relative, consumer_root=None):
require(isinstance(relative, str) and 0 < len(relative) <= 240
and re.fullmatch(r'[A-Za-z0-9_./-]+', relative)
and all(part not in {'', '.', '..'} for part in relative.split('/')), 'Unsafe receipt path')
root = (consumer_root or Path.cwd()).resolve()
path = root / relative
require(path.resolve().is_relative_to(root), 'Receipt escapes consumer root')
content = (json.dumps(event, indent=2, allow_nan=False) + '\n').encode()
require(len(content) <= 131072, 'Receipt exceeds collector size limit')
path.parent.mkdir(parents=True, exist_ok=True)
with path.open('xb') as stream:
stream.write(content)
return {'path': relative, 'sha256': hashlib.sha256(content).hexdigest()}
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--version', help='Published version; defaults to current pointer')
commands = parser.add_subparsers(dest='command', required=True)
query = commands.add_parser('lookup', help='Intersect force/responsibility filters; candidate IDs form an explicit allowlist')
query.add_argument('--candidate', action='append', default=[])
query.add_argument('--force', action='append', default=[])
query.add_argument('--responsibility', action='append', default=[])
prepare = commands.add_parser('receipt', help='Prepare a consumer-authored event with verified release pins')
prepare.add_argument('--input', required=True, type=Path, help='Decision/provenance JSON; see feedback/decision-input.example.json')
prepare.add_argument('--output', help='New source-relative receipt path under current consumer directory; never overwrite')
args = parser.parse_args()
try:
index, pin, manifest = published(version=args.version)
if args.command == 'lookup':
result = lookup(index, pin, manifest, args.candidate, args.force, args.responsibility)
else:
require(args.input.stat().st_size <= 131072, 'Decision input too large')
event = receipt(json.loads(args.input.read_text()), index, pin)
result = write_receipt(event, args.output) if args.output else event
print(json.dumps(result, indent=2, allow_nan=False))
except (ValueError, TypeError, KeyError, OSError) as exc:
parser.exit(1, f'Reference consumption failed: {exc}\n')
if __name__ == '__main__':
main()

View file

@ -2,6 +2,7 @@
# Executable schema-v1 event validation/collection: activity-core
# src/activity_core/context_resolvers/frontend_patterns.py (validate_event).
# Consumer closeout integration and real-use acceptance remain open in T02.
# Local producer: scripts/reference_consume.py receipt (explicit JSON decision input).
# Consumer produces immutable events; collector supplies independently verified origin.
schema_version: 1
use_id: "<consumer-repo>:<task-id>:<decision-id>"

View file

@ -0,0 +1,176 @@
"""Exercise qualified lookup, immutable event production and owner compatibility."""
from copy import deepcopy
import hashlib
import json
from pathlib import Path
import shutil
import subprocess
import sys
import pytest
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / 'scripts'))
import reference_consume as consume
@pytest.fixture(scope='module')
def published():
return consume.published()
@pytest.fixture
def decision():
return json.loads((ROOT / 'feedback/decision-input.example.json').read_text())
def test_lookup_explains_filters_and_preserves_exact_read_revisions(published):
index, pin, manifest = published
result = consume.lookup(index, pin, manifest, ['CAND-0007'], ['recoverability'])
candidate, = result['candidates']
assert result['status'] == 'matches'
assert candidate['id'] == 'CAND-0007'
assert candidate['classification']['forces']['worsens']
assert candidate['gaps'] and candidate['maturity'] == 'hypothesis'
assert candidate['contract']['revision'] == manifest['corpus_revision']
assert f'/commit/{manifest["corpus_revision"]}/' in candidate['contract']['url']
assert all(e['locator']['revision'] == pin['packaging_revision'] for e in candidate['evidence'])
assert all(e['status'] == 'proposed' and e['condition'] and e['unknown'] for e in result['relations'])
assert all(v['revision'] == pin['packaging_revision'] for v in result['entrypoints'].values())
assert result['reference']['manifest_sha256'] == pin['manifest_sha256']
assert consume.lookup(index, pin, manifest, ['CAND-0007'], ['telemetry-quality'])['status'] == 'no-match'
def test_missing_context_abstains_and_responsibility_filters_intersect(published):
index, pin, manifest = published
result = consume.lookup(index, pin, manifest)
assert result['status'] == 'needs-context' and result['candidates'] == result['relations'] == []
result = consume.lookup(index, pin, manifest, responsibilities=['mutation'], forces=['recoverability'])
assert result['candidates']
assert all('mutation' in c['classification']['responsibilities'] and
'recoverability' in c['classification']['forces']['improves'] for c in result['candidates'])
@pytest.mark.parametrize('filters', [
{'candidates': ['CAND-0018']}, {'forces': ['magic']}, {'responsibilities': ['magic']},
])
def test_unknown_or_unpublished_identity_is_not_silently_selected(published, filters):
with pytest.raises(ValueError, match='Unknown'):
consume.lookup(*published, **filters)
def test_live_research_is_independent_and_tampered_export_fails(tmp_path, published):
shutil.copytree(ROOT / 'reference', tmp_path / 'reference')
(tmp_path / '.git').symlink_to(ROOT / '.git', target_is_directory=True)
live = tmp_path / 'research/candidates/MutationOutcomeTracking.md'
live.parent.mkdir(parents=True)
live.write_text('Legitimate later research outside the frozen release.\n')
assert consume.published(tmp_path) == published
export = tmp_path / 'reference/0.1/index.json'
export.write_text('{}\n')
with pytest.raises(ValueError, match='Published export changed'):
consume.published(tmp_path)
with pytest.raises(ValueError, match='Unknown reference version'):
consume.published(tmp_path, '9.9')
def test_receipt_keeps_unknown_outcome_and_pins_verified_reference(published, decision):
index, pin, _ = published
original = deepcopy(decision)
event = consume.receipt(decision, index, pin)
assert decision == original
assert event['kind'] == 'synthetic'
assert event['outcome']['value'] == 'unknown'
assert event['outcome']['observation'] is None
assert event['outcome']['measured_decision_minutes'] is None
assert event['reference']['packaging_revision'] == pin['packaging_revision']
assert 'origin' not in event
@pytest.mark.parametrize('mutate', [
lambda d: d.update(kind=''),
lambda d: d.update(recorded_at='2999-01-01T00:00:00Z'),
lambda d: d.update(recorded_at='2026-01-01T00:00:00'),
lambda d: d.update(previous_event_sha256='invalid'),
lambda d: d.update(origin={'verified': True}),
lambda d: d['consumer'].update(revision='main'),
lambda d: d['consumer'].update(run_ref=''),
lambda d: d['context'].update(considered=['CAND-0018']),
lambda d: d['context'].update(selected=['CAND-0007']),
lambda d: d['context'].update(considered=['CAND-0007', 'CAND-0007']),
lambda d: d['decision'].update(action='applied'),
lambda d: d['outcome'].update(value='helpful'),
lambda d: d['outcome'].update(measured_decision_minutes=-1),
lambda d: d['outcome'].update(measured_rework_minutes=float('nan')),
])
def test_invalid_producer_claims_or_provenance_are_rejected(published, decision, mutate):
mutate(decision)
with pytest.raises(ValueError):
consume.receipt(decision, *published[:2])
def test_followup_and_observed_result_retain_lineage(published, decision):
decision['previous_event_sha256'] = 'a' * 64
decision['event_id'] = 'fixture-outcome-02'
decision['outcome'].update(value='mixed', basis='observed-result', stage='test',
observation='Disclosed fixture result; no consumer use.', evidence_refs=['fixture:test-01'])
event = consume.receipt(decision, *published[:2])
assert event['previous_event_sha256'] == 'a' * 64
assert event['outcome']['value'] == 'mixed'
def test_immutable_write_returns_exact_collector_index_entry(tmp_path, published, decision):
event = consume.receipt(decision, *published[:2])
path = 'research/pattern-use/fixture/event-01.json'
entry = consume.write_receipt(event, path, tmp_path)
raw = (tmp_path / path).read_bytes()
assert json.loads(raw) == event
assert entry == {'path': path, 'sha256': hashlib.sha256(raw).hexdigest()}
with pytest.raises(FileExistsError):
consume.write_receipt(event, path, tmp_path)
assert (tmp_path / path).read_bytes() == raw
@pytest.mark.parametrize('path', ['/tmp/event.json', '../event.json', 'x//event.json', 'x/./event.json', 'x\\event.json'])
def test_unsafe_paths_are_rejected_before_writing(tmp_path, published, decision, path):
with pytest.raises(ValueError, match='Unsafe receipt path'):
consume.write_receipt(consume.receipt(decision, *published[:2]), path, tmp_path)
assert list(tmp_path.iterdir()) == []
def test_symlink_escape_is_refused(tmp_path, published, decision):
(tmp_path / 'out').symlink_to(ROOT, target_is_directory=True)
with pytest.raises(ValueError, match='escapes consumer root'):
consume.write_receipt(consume.receipt(decision, *published[:2]), 'out/event.json', tmp_path)
def test_cli_generates_owner_accepted_event_and_index(tmp_path):
result = subprocess.run([sys.executable, str(ROOT / 'scripts/reference_consume.py'), '--version', '0.1', 'receipt',
'--input', str(ROOT / 'feedback/decision-input.example.json'), '--output', 'use/event.json'],
cwd=tmp_path, capture_output=True, text=True, check=True)
entry = json.loads(result.stdout)
assert entry['sha256'] == hashlib.sha256((tmp_path / entry['path']).read_bytes()).hexdigest()
owner = ROOT.parent / 'activity-core'
python = owner / '.venv/bin/python'
if not python.exists():
pytest.skip('Actual activity-core contract check requires the sibling owner environment')
script = '''import json, sys
sys.path.insert(0, sys.argv[1])
from activity_core.context_resolvers.frontend_patterns import validate_event
event = json.load(open(sys.argv[2]))
validate_event(event, event['consumer']['repo'])
'''
subprocess.run([str(python), '-B', '-c', script, str(owner / 'src'), str(tmp_path / entry['path'])],
check=True, capture_output=True, text=True)
def test_invalid_cli_decision_cannot_create_a_receipt(tmp_path, decision):
decision['decision']['action'] = 'applied' # Selection remains empty.
source = tmp_path / 'decision.json'
source.write_text(json.dumps(decision))
result = subprocess.run([sys.executable, str(ROOT / 'scripts/reference_consume.py'), 'receipt',
'--input', str(source), '--output', 'use/event.json'],
cwd=tmp_path, capture_output=True, text=True)
assert result.returncode == 1
assert 'Action/selection mismatch' in result.stderr
assert not (tmp_path / 'use').exists()

View file

@ -58,6 +58,13 @@ Done: published/discoverable reference plus first real pinned decision note, and
release validation survives legitimate new research. Registry entry/index are prepared
in this design session; hosted registration and consumer integration remain open.
Residual acceptance received from FEP-WP-0010 (2026-10-04): the verified local
[lookup/citation helper](../scripts/reference_consume.py) and
[consumer commands](../feedback/ConsumerIntegration.md#local-lookup-and-closeout-helper)
remove local reference-navigation preparation. T01 still owns hosted registration,
common-template-owner integration into an actual eligible frontend task and its
first pinned decision note. No fixture or audit of this repo supplies that receipt.
## Capture evidence of use and outcomes
```task
@ -87,6 +94,14 @@ rejected/no-match, delayed/corrected, duplicate and malformed paths tested as fi
No fabricated real outcome to close the task. Pending cases get one automated artifact
refresh after 14 days and remain unknown after 30; no manual survey dependency.
Residual acceptance received from FEP-WP-0010 (2026-10-04): the local producer now
fills verified release pins and writes a new schema-v1 JSON/YAML event with an exact
index digest; its synthetic event passes activity-core's actual validator. T02 retains
the consumer-owned automatic closeout invocation/index commit, authorized enrollment,
real use plus linked inspectable outcome, and delayed-artifact refresh. Collector
validation is not outcome truth or a producer-origin verification claim. Existing
collection remains with activity-core; no second collector or fictional use was added.
## Run bounded improvements and monthly review autonomously
```task

View file

@ -4,7 +4,7 @@ type: workplan
title: "Close local reference-consumption gaps"
domain: infotech
repo: frontend-patterns
status: ready
status: finished
owner: codex
topic_slug: canon
created: "2026-10-04"
@ -38,7 +38,7 @@ independent/held-out evidence. Name existing owners for the most relevant gaps.
```task
id: FEP-WP-0010-T02
status: todo
status: done
priority: high
state_hub_task_id: "52e73505-dadc-52b9-aedb-4ba0889e580c"
```
@ -54,7 +54,7 @@ release validation and fail on tampering or unavailable pins.
```task
id: FEP-WP-0010-T03
status: todo
status: done
priority: high
state_hub_task_id: "08762144-141e-5f13-ab58-2054da04a221"
```
@ -71,7 +71,7 @@ contract instead of creating a second collection service.
```task
id: FEP-WP-0010-T04
status: todo
status: done
priority: high
state_hub_task_id: "7efb810a-d6f8-540c-90a5-3c333e0bdbea"
```
@ -89,3 +89,30 @@ WP-0003/0004 (broader/gated applicability) and WP-0006 (comparable windows). Add
record references there. This plan may finish its local deliverable while the empirical
owners remain open; do not claim those gaps closed or leave actionable residuals only
in prose/history.
## Completion — 2026-10-04
The local consume → cite → prepare-receipt bridge is delivered. `reference_consume.py`
verifies the immutable preview before filtering or receipt preparation; results carry
corpus/packaging pins, force costs, proposed relation conditions and explicit limits.
Input validation precedes exclusive source-relative writes; returned digests identify
the exact JSON/YAML bytes. The consumer retains its own decision and evidence authority.
Validation: 104 tests pass with zero skips (31 consumption, 2 schema, 33 bench/admission,
22 evolution, 16 preview), including actual activity-core validator acceptance at
`3dd68d1`. Synthetic bench planning and evolution/classification/reference checks pass.
[Verification record](../docs/evidence/2026-10-04-reference-consumption.json) preserves
owner hash and boundaries. No paid inference, production mutation, enrolled consumer,
new empirical learning cycle, canonical admission or usefulness claim was introduced.
Residual acceptance was handed to the existing live WP-0008 T01/T02 descriptions
before finish: actual hosted/template/consumer decision integration, hook invocation,
index/enrollment, real use plus outcome and delayed refresh. WP-0009 retains independent
human/fresh replication/M1; WP-0003/0004 retains contrasting/gated applicability;
WP-0006 retains useful comparable returns evidence. Those existing obligations are not
new duplicate tasks and are not closed by the bridge's disclosed development cases.
Scope/assessment and capability guidance now describe this separation accurately.
State Hub implementation decision: `6914093c-e540-4d31-a1ea-607021c5e185`; plan UUID
`c46b02ca-26a8-5313-b0f2-eb0385ff820c`. Registration assigned all task UUIDs and verified
the initial pushed plan on primary/railiance01; final source statuses are synced at close.