diff --git a/Makefile b/Makefile index 9d191f0..36785e1 100644 --- a/Makefile +++ b/Makefile @@ -19,3 +19,10 @@ evolution-check: evolution-test: PYTHONDONTWRITEBYTECODE=1 "$(BENCH_PY)" -m pytest -q -p no:cacheprovider tests/test_research_evolution.py + +.PHONY: classification-report classification-check +classification-report: + python3 scripts/classification_coverage.py + +classification-check: + python3 scripts/classification_coverage.py --check diff --git a/TREE.md b/TREE.md index 3a042d5..7288b35 100644 --- a/TREE.md +++ b/TREE.md @@ -261,6 +261,8 @@ research/candidates/VersionCheckedMutation.md research/candidates/VerticalFeatureSlice.md research/cases/CarniatoGrandUnifyingArchitecture.md research/cases/LocalFirstAuthority.md +research/catalog/ClassificationCoverage.json +research/catalog/ClassificationCoverage.md research/catalog/PatternCandidateCatalog.md research/catalog/SourceCatalog.md research/compendium/FrontendArchitectureCompendium.md @@ -422,6 +424,7 @@ research/experiments/navigation-history/ownership.mjs research/experiments/navigation-history/package-lock.json research/experiments/navigation-history/package.json research/experiments/navigation-history/result.json +research/plan/AuthorityProjectionComparison.md research/plan/OpenQuestions.md research/plan/ResearchProgram.md research/plan/ResearchProtocol.md @@ -474,6 +477,7 @@ research/walkthroughs/VC01-TransactionalAdministration.md research/walkthroughs/VC02-OfflineFieldNotes.md research/walkthroughs/VC03-ContentPublication.md research/walkthroughs/VC06-MicrofrontendEstate.md +scripts/classification_coverage.py scripts/research_admission.py scripts/research_bench.py scripts/research_evolution.py diff --git a/research/catalog/ClassificationCoverage.json b/research/catalog/ClassificationCoverage.json new file mode 100644 index 0000000..9c35100 --- /dev/null +++ b/research/catalog/ClassificationCoverage.json @@ -0,0 +1,330 @@ +{ + "schema_version": 1, + "assessed_at": "2026-09-27", + "assessor": "Codex", + "status": "provisional author-model classification", + "basis_revision": "9ac6f6925be6743113338ad140f8465b2d99a404", + "counting_rule": "Count a candidate once per assigned label; labels overlap. Responsibilities describe the direct mechanism, not every prerequisite or downstream effect. Forces are selected trade-offs, not exhaustive coverage or measured outcomes.", + "candidates": [ + { + "path": "research/candidates/ConflictCopy.md", + "sha256": "24f250d89ac17d96df287d67c275b5320b48c8356b57ae126a6c941e92a1c6e9", + "title": "Conflict Copy", + "responsibilities": [ + "reconciliation" + ], + "forces": { + "improves": [ + "recoverability", + "information-completeness" + ], + "worsens": [ + "cognitive-load", + "reconciliation-complexity" + ] + }, + "rationale": "Preserves divergent content for later semantic resolution; unresolved copies impose review and merge work." + }, + { + "path": "research/candidates/DerivedState.md", + "sha256": "9f29b9f1296a52b83f08dfd97437c4dc16e4949ddac1ff3b3830e3fc9831bd4f", + "title": "Derived State", + "responsibilities": [ + "projection" + ], + "forces": { + "improves": [ + "authoritative-consistency" + ], + "worsens": [ + "client-compute-cost", + "cache-invalidation-complexity" + ] + }, + "rationale": "Projects coherent inputs without separately mutable derived copies; recomputation and cached dependency tracking cost work." + }, + { + "path": "research/candidates/ExplicitReconciliation.md", + "sha256": "d48a40aab7417b8b8f8406094c84b9e90d0ca8c2f94d6e616d0b80499dc04959", + "title": "Explicit Reconciliation", + "responsibilities": [ + "reconciliation", + "affordance" + ], + "forces": { + "improves": [ + "information-completeness", + "recoverability" + ], + "worsens": [ + "cognitive-load", + "continuity-of-interaction" + ] + }, + "rationale": "Makes semantic resolution an explicit comparison task; deciding among versions interrupts ordinary work." + }, + { + "path": "research/candidates/FailureBoundary.md", + "sha256": "a6e76ed17a92baade683d5df96fa8ecd066c593661e6a14983a86d046e7d9c2c", + "title": "Failure Boundary", + "responsibilities": [ + "composition", + "affordance" + ], + "forces": { + "improves": [ + "fault-containment", + "continuity-of-interaction" + ], + "worsens": [ + "implementation-complexity" + ] + }, + "rationale": "Contains declared failures and supplies recovery interaction; partial-state recovery and escalation add complexity." + }, + { + "path": "research/candidates/FocusReturn.md", + "sha256": "9149c2f563eafcfec983a9285949225840d97804568309bd16bbb1eb6b3dde77", + "title": "Focus Return", + "responsibilities": [ + "affordance" + ], + "forces": { + "improves": [ + "continuity-of-interaction", + "accessibility" + ], + "worsens": [ + "implementation-complexity" + ] + }, + "rationale": "Restores a meaningful keyboard continuation; disappearing targets and nested ownership require lifecycle handling." + }, + { + "path": "research/candidates/IndependentFeatureBoundary.md", + "sha256": "60b78725ed4036681675d4c68244fc25aea6e0dffb09d54a7108e3fcbf527b4f", + "title": "Independent Feature Boundary", + "responsibilities": [ + "composition", + "execution" + ], + "forces": { + "improves": [ + "independent-deployability", + "team-autonomy" + ], + "worsens": [ + "compatibility-pressure", + "deployment-complexity" + ] + }, + "rationale": "Separates release and activation through composition contracts; version compatibility and session transitions add obligations." + }, + { + "path": "research/candidates/InvalidateAndReproject.md", + "sha256": "47c405adbc3caac2383768ed3d30b6a2e7ff1f44f4954c340049e78944227844", + "title": "Invalidate and Reproject", + "responsibilities": [ + "synchronization", + "projection" + ], + "forces": { + "improves": [ + "freshness" + ], + "worsens": [ + "bandwidth-cost", + "cache-invalidation-complexity" + ] + }, + "rationale": "Refreshes affected observations after accepted writes; scope and ordering of network/cache work need explicit management." + }, + { + "path": "research/candidates/IslandsOfInteractivity.md", + "sha256": "821a1497da10c02ae11fa6f56cb8e205a4f32c40b39770ab8ca3f907561a260a", + "title": "Islands of Interactivity", + "responsibilities": [ + "projection", + "execution" + ], + "forces": { + "improves": [ + "client-compute-cost" + ], + "worsens": [ + "implementation-complexity" + ] + }, + "rationale": "Scopes activation to selected regions; intended compute savings compete with loading and inter-region coordination. Savings require measurement." + }, + { + "path": "research/candidates/LocalCommitBoundary.md", + "sha256": "22165fc2979e8f4d8859b34417cb71e7b5ec7a8440ad045aee2383d499648013", + "title": "Local Commit Boundary", + "responsibilities": [ + "authority", + "mutation" + ], + "forces": { + "improves": [ + "recoverability", + "offline-usability" + ], + "worsens": [ + "implementation-complexity", + "perceived-responsiveness" + ] + }, + "rationale": "Defines acknowledged local acceptance separately from visual input; atomic persistence and waiting for completion add cost." + }, + { + "path": "research/candidates/LocalDraftState.md", + "sha256": "8e5f427975c76083c80b895bc68b7130b249be5129c2854f3df11cc374b4b240", + "title": "Local Draft State", + "responsibilities": [ + "affordance", + "mutation" + ], + "forces": { + "improves": [ + "recoverability", + "continuity-of-interaction" + ], + "worsens": [ + "implementation-complexity", + "privacy" + ] + }, + "rationale": "Preserves editable intent separately from refreshed observations; lifetime management adds complexity and sensitive-data retention risk." + }, + { + "path": "research/candidates/MutationOutcomeTracking.md", + "sha256": "e04cb835fd054bfaf88616375bd0262deffb2801ea04105be92543bfa9582179", + "title": "Mutation Outcome Tracking", + "responsibilities": [ + "mutation", + "reconciliation" + ], + "forces": { + "improves": [ + "recoverability", + "information-completeness" + ], + "worsens": [ + "implementation-complexity" + ] + }, + "rationale": "Tracks uncertain write outcomes and resolves them through receipt lookup or bounded replay; identity/retention state adds cost." + }, + { + "path": "research/candidates/PendingSynchronization.md", + "sha256": "9c5cb0f4ea4e6f4406ce55513ca2c252172427a49d8280ae1a9c713f1d11b070", + "title": "Pending Synchronization", + "responsibilities": [ + "synchronization" + ], + "forces": { + "improves": [ + "offline-usability", + "recoverability" + ], + "worsens": [ + "freshness", + "reconciliation-complexity" + ] + }, + "rationale": "Keeps locally accepted work queued across disconnection; remote freshness is delayed and retry/reconciliation contracts are required." + }, + { + "path": "research/candidates/PresentationModel.md", + "sha256": "5ff03b5799b0a9e5ec6b54b21034720d89dbc8ffde7c4cdcf71616cdf57baf49", + "title": "Presentation Model", + "responsibilities": [ + "projection", + "affordance" + ], + "forces": { + "improves": [ + "testability", + "portability" + ], + "worsens": [ + "implementation-complexity" + ] + }, + "rationale": "Separates presentation decisions from widget APIs; model/view binding and synchronization add a failure surface." + }, + { + "path": "research/candidates/ProgressiveEnhancement.md", + "sha256": "1dc3885ef896b9a7cb61af11fa8893484eb1e13454207326abc7250d5ca4382b", + "title": "Progressive Enhancement", + "responsibilities": [ + "execution", + "affordance" + ], + "forces": { + "improves": [ + "recoverability" + ], + "worsens": [ + "implementation-complexity" + ] + }, + "rationale": "Preserves an essential baseline when optional scripts fail; equivalent task semantics and recovery across paths add design work." + }, + { + "path": "research/candidates/URLAddressableState.md", + "sha256": "07d524c3c0dd080bd42ff5dd79a36e6ecd2a8654f07ece880b483477f7ca4497", + "title": "URL-addressable State", + "responsibilities": [ + "address" + ], + "forces": { + "improves": [ + "addressability" + ], + "worsens": [ + "privacy", + "compatibility-pressure" + ] + }, + "rationale": "Makes selected views reconstructible from addresses; exposure and evolving URL contracts constrain what can be encoded." + }, + { + "path": "research/candidates/VersionCheckedMutation.md", + "sha256": "6a3335ae2fa1de51c95dcbfa8d766d988a3f7a9604fb608b8a0bcf9a10b64198", + "title": "Version-Checked Mutation", + "responsibilities": [ + "authority", + "mutation", + "reconciliation" + ], + "forces": { + "improves": [ + "authoritative-consistency" + ], + "worsens": [ + "cognitive-load" + ] + }, + "rationale": "Checks stale bases at commit and exposes rejected proposals for reconsideration; contention adds repeated user review." + }, + { + "path": "research/candidates/VerticalFeatureSlice.md", + "sha256": "d6b332b9c104a079bd4b72512b8ddd1a889199b2e2e531753c411688053e647a", + "title": "Vertical Feature Slice", + "responsibilities": [ + "composition" + ], + "forces": { + "improves": [ + "feature-change-frequency" + ], + "worsens": [ + "implementation-complexity" + ] + }, + "rationale": "Localizes a capability change behind an interface; shared concepts and interfaces require maintenance. Benefit means coping with frequent changes, not changing their frequency." + } + ] +} diff --git a/research/catalog/ClassificationCoverage.md b/research/catalog/ClassificationCoverage.md new file mode 100644 index 0000000..ea2bf9c --- /dev/null +++ b/research/catalog/ClassificationCoverage.md @@ -0,0 +1,147 @@ +# Classification coverage + +Generated by `python3 scripts/classification_coverage.py`; assessment: 2026-09-27. + +17 developed drafts; 16 original seeds at `2a5dabd`. 7 original seeds developed, 9 without dedicated drafts, 10 later additions. All current candidates remain hypotheses; classification confers no admission or new evidence. + +Families/scales come from current candidate metadata. Responsibilities and forces are provisional author-model interpretations in [ClassificationCoverage.json](ClassificationCoverage.json), with candidate hashes and per-record rationale. They are separate from canonical metadata. + +Count a candidate once per assigned label; labels overlap. Responsibilities describe the direct mechanism, not every prerequisite or downstream effect. Forces are selected trade-offs, not exhaustive coverage or measured outcomes. + +A zero means no assigned direct coverage, not absence of discussion. Force direction describes an intended favorable effect or accepted cost, not a measured result. A classification refresh requires reading changed candidates; do not merely replace hashes. + +## Families + +| Family | Original seeds | Developed drafts | +| --- | ---: | ---: | +| authority | 3 | 2 | +| address | 1 | 1 | +| projection | 4 | 4 | +| affordance | 2 | 6 | +| mutation | 0 | 4 | +| synchronization | 2 | 5 | +| composition | 2 | 4 | +| execution | 0 | 3 | +| quality | 2 | 3 | + +## Scales + +| Scale | Original seeds | Developed drafts | +| --- | ---: | ---: | +| L0 | 0 | 0 | +| L1 | 0 | 1 | +| L2 | 3 | 6 | +| L3 | 7 | 5 | +| L4 | 2 | 3 | +| L5 | 1 | 2 | +| L6 | 4 | 13 | +| L7 | 0 | 0 | +| cross-scale | 1 | 0 | + +## Responsibilities + +These are direct-mechanism assignments, not family aliases. In particular, reconciliation is a responsibility and quality is a family. + +| Responsibility | Drafts | +| --- | ---: | +| address | 1 | +| authority | 2 | +| projection | 4 | +| affordance | 6 | +| mutation | 4 | +| synchronization | 2 | +| reconciliation | 4 | +| composition | 3 | +| execution | 3 | + +## Forces + +Selected trade-offs only. Counts are unique candidates per force, even if a force appears on both sides. Unassigned forces remain research questions; the mapping does not certify a benefit. + +| Force | Intended benefit | Cost / tension | Distinct drafts | +| --- | ---: | ---: | ---: | +| interaction-latency | 0 | 0 | 0 | +| perceived-responsiveness | 0 | 1 | 1 | +| continuity-of-interaction | 3 | 1 | 4 | +| discoverability | 0 | 0 | 0 | +| addressability | 1 | 0 | 1 | +| accessibility | 1 | 0 | 1 | +| recoverability | 7 | 0 | 7 | +| offline-usability | 2 | 0 | 2 | +| authoritative-consistency | 2 | 0 | 2 | +| freshness | 1 | 1 | 2 | +| cacheability | 0 | 0 | 0 | +| conflict-probability | 0 | 0 | 0 | +| collaborative-concurrency | 0 | 0 | 0 | +| data-locality | 0 | 0 | 0 | +| privacy | 0 | 2 | 2 | +| information-completeness | 3 | 0 | 3 | +| first-content-latency | 0 | 0 | 0 | +| hydration-cost | 0 | 0 | 0 | +| client-compute-cost | 1 | 1 | 2 | +| server-compute-cost | 0 | 0 | 0 | +| bandwidth-cost | 0 | 1 | 1 | +| connection-lifetime | 0 | 0 | 0 | +| network-reliability | 0 | 0 | 0 | +| startup-cost | 0 | 0 | 0 | +| incremental-update-cost | 0 | 0 | 0 | +| feature-change-frequency | 1 | 0 | 1 | +| schema-change-frequency | 0 | 0 | 0 | +| independent-deployability | 1 | 0 | 1 | +| team-autonomy | 1 | 0 | 1 | +| release-coupling | 0 | 0 | 0 | +| compatibility-pressure | 0 | 2 | 2 | +| implementation-complexity | 0 | 9 | 9 | +| reconciliation-complexity | 0 | 2 | 2 | +| cognitive-load | 0 | 3 | 3 | +| testability | 1 | 0 | 1 | +| observability | 0 | 0 | 0 | +| debuggability | 0 | 0 | 0 | +| portability | 1 | 0 | 1 | +| security-exposure | 0 | 0 | 0 | +| scalability | 0 | 0 | 0 | +| fault-containment | 1 | 0 | 1 | +| deployment-complexity | 0 | 1 | 1 | +| cache-invalidation-complexity | 0 | 2 | 2 | +| telemetry-quality | 0 | 0 | 0 | +| incident-recoverability | 0 | 0 | 0 | + +## Per-candidate responsibility and force rationale + +| Candidate | Responsibilities | Intended benefit | Cost / tension | Rationale | +| --- | --- | --- | --- | --- | +| [Conflict Copy](../candidates/ConflictCopy.md) | reconciliation | recoverability, information-completeness | cognitive-load, reconciliation-complexity | Preserves divergent content for later semantic resolution; unresolved copies impose review and merge work. | +| [Derived State](../candidates/DerivedState.md) | projection | authoritative-consistency | client-compute-cost, cache-invalidation-complexity | Projects coherent inputs without separately mutable derived copies; recomputation and cached dependency tracking cost work. | +| [Explicit Reconciliation](../candidates/ExplicitReconciliation.md) | reconciliation, affordance | information-completeness, recoverability | cognitive-load, continuity-of-interaction | Makes semantic resolution an explicit comparison task; deciding among versions interrupts ordinary work. | +| [Failure Boundary](../candidates/FailureBoundary.md) | composition, affordance | fault-containment, continuity-of-interaction | implementation-complexity | Contains declared failures and supplies recovery interaction; partial-state recovery and escalation add complexity. | +| [Focus Return](../candidates/FocusReturn.md) | affordance | continuity-of-interaction, accessibility | implementation-complexity | Restores a meaningful keyboard continuation; disappearing targets and nested ownership require lifecycle handling. | +| [Independent Feature Boundary](../candidates/IndependentFeatureBoundary.md) | composition, execution | independent-deployability, team-autonomy | compatibility-pressure, deployment-complexity | Separates release and activation through composition contracts; version compatibility and session transitions add obligations. | +| [Invalidate and Reproject](../candidates/InvalidateAndReproject.md) | synchronization, projection | freshness | bandwidth-cost, cache-invalidation-complexity | Refreshes affected observations after accepted writes; scope and ordering of network/cache work need explicit management. | +| [Islands of Interactivity](../candidates/IslandsOfInteractivity.md) | projection, execution | client-compute-cost | implementation-complexity | Scopes activation to selected regions; intended compute savings compete with loading and inter-region coordination. Savings require measurement. | +| [Local Commit Boundary](../candidates/LocalCommitBoundary.md) | authority, mutation | recoverability, offline-usability | implementation-complexity, perceived-responsiveness | Defines acknowledged local acceptance separately from visual input; atomic persistence and waiting for completion add cost. | +| [Local Draft State](../candidates/LocalDraftState.md) | affordance, mutation | recoverability, continuity-of-interaction | implementation-complexity, privacy | Preserves editable intent separately from refreshed observations; lifetime management adds complexity and sensitive-data retention risk. | +| [Mutation Outcome Tracking](../candidates/MutationOutcomeTracking.md) | mutation, reconciliation | recoverability, information-completeness | implementation-complexity | Tracks uncertain write outcomes and resolves them through receipt lookup or bounded replay; identity/retention state adds cost. | +| [Pending Synchronization](../candidates/PendingSynchronization.md) | synchronization | offline-usability, recoverability | freshness, reconciliation-complexity | Keeps locally accepted work queued across disconnection; remote freshness is delayed and retry/reconciliation contracts are required. | +| [Presentation Model](../candidates/PresentationModel.md) | projection, affordance | testability, portability | implementation-complexity | Separates presentation decisions from widget APIs; model/view binding and synchronization add a failure surface. | +| [Progressive Enhancement](../candidates/ProgressiveEnhancement.md) | execution, affordance | recoverability | implementation-complexity | Preserves an essential baseline when optional scripts fail; equivalent task semantics and recovery across paths add design work. | +| [URL-addressable State](../candidates/URLAddressableState.md) | address | addressability | privacy, compatibility-pressure | Makes selected views reconstructible from addresses; exposure and evolving URL contracts constrain what can be encoded. | +| [Version-Checked Mutation](../candidates/VersionCheckedMutation.md) | authority, mutation, reconciliation | authoritative-consistency | cognitive-load | Checks stale bases at commit and exposes rejected proposals for reconsideration; contention adds repeated user review. | +| [Vertical Feature Slice](../candidates/VerticalFeatureSlice.md) | composition | feature-change-frequency | implementation-complexity | Localizes a capability change behind an interface; shared concepts and interfaces require maintenance. Benefit means coping with frequent changes, not changing their frequency. | + +## Original seeds without dedicated drafts + +- Server Authority +- Local Authority +- Replicated Authority +- Server Projection +- Client Projection +- Streaming Projection +- Local Interaction State +- Optimistic Overlay +- Persistent Subscription + +## Research implication + +The developed corpus concentrates on interaction/recovery; authority placement and basic projection choices remain underdeveloped. Family membership alone hides this gap. The next bounded authority/projection comparison is specified in [the research brief](../plan/AuthorityProjectionComparison.md). Keep the navigation fixture paused; pending Zulip applicability questions remain in WP-0003 T01. + +This inventory is a coverage aid, not a maturity score, empirical learning cycle, graph export or proof that the initial ontology is sufficient. diff --git a/research/catalog/PatternCandidateCatalog.md b/research/catalog/PatternCandidateCatalog.md index 6981893..fa1995e 100644 --- a/research/catalog/PatternCandidateCatalog.md +++ b/research/catalog/PatternCandidateCatalog.md @@ -2,6 +2,10 @@ This file tracks concepts before promotion into canonical pattern files. +See [ClassificationCoverage.md](ClassificationCoverage.md) for current counts by +family, scale, responsibility and force, and the comparison against the initial +sixteen seeds. Responsibility/force assignments are provisional editorial mappings. + | Candidate | Family | Scale | Status | Main force | |---|---|---:|---|---| | Server Authority | authority | L2 | seed | authoritative consistency | diff --git a/research/plan/AuthorityProjectionComparison.md b/research/plan/AuthorityProjectionComparison.md new file mode 100644 index 0000000..9d90483 --- /dev/null +++ b/research/plan/AuthorityProjectionComparison.md @@ -0,0 +1,62 @@ +# Next bounded comparison — authority placement and projection + +Prepared: 2026-09-27 · WP-0003 T01 · Status: ready for prospective registration. +This brief selects the next breadth investigation; no source investigation or +empirical cycle has been executed by writing it. + +## Why this question + +The [classification inventory](../catalog/ClassificationCoverage.md) finds that +seven of the original sixteen seeds have developed drafts. None of Server Authority, +Local Authority, Replicated Authority, Server Projection, Client Projection or +Streaming Projection has a dedicated developed draft. Current authority-family +membership instead describes acceptance/precondition mechanisms. The language needs +to explain placement decisions as well as recovery after those decisions. + +Do these six seed names describe reusable patterns, context choices, implementation +strategies or delivery mechanisms? Which independent decisions are conflated when +we pair server authority with server rendering, or local authority with client rendering? + +## Bounded method + +1. Register one prospective cycle before new evidence collection, with a new + comparison group for this documentary/conceptual protocol. Record effort and + source access limits; do not pool it with navigation runtime cycles. +2. Reuse existing Carniato/local-first and navigation/rendering reviews as prior + interpretations, not new independent evidence. Inspect at most six primary-source + documents across at least two independent source families. Pin versions or record + access dates and claim locators. Sources may corroborate only parts of a contract. +3. For each of the six seeds, describe the relevant authority scope, acceptance + boundary, replica/cache semantics, projection inputs, execution location and + delivery timing. Keep optimistic display distinct from accepted authority and + streaming delivery distinct from where projection is computed. +4. Construct a decision matrix for two synthetic briefs already represented by + VC-01 transactional administration and VC-02 offline field notes. Vary projection + placement while holding authority assumptions explicit. State incompatible or + unsupported combinations rather than invent implementations. These are conceptual + probes, not new validated walkthroughs or deployed known uses. +5. Record a disposition for each seed: develop a candidate, retain as a seed, treat + as context/strategy, merge, or reject—with evidence and unresolved questions. + Produce at most two new drafts, and only where context, competing forces, + consequences and alternatives justify distinct patterns. Zero is a valid result. + +## Acceptance, limits and handoff + +Deliver a claim table, the two decision traces and six seed dispositions. Identify +at least one conflation or counterexample, or explicitly report that none was located +in the bounded evidence. Review responsibility/force mappings against resulting +candidate changes, then regenerate coverage and update evolution profiles/ledger at +the normal review boundary. No automatic canonical promotion or gate closure. + +Stop after six primary documents or 45 active research minutes, whichever occurs +first; allow up to 10 additional verification minutes. Register these limits with the +cycle before starting. Preserve unknowns instead of widening silently. Live inference, +if needed later, uses llm-connect; this brief requires no model calls or new runtime +fixture. It does not establish measured performance, production guarantees or human +usefulness. + +WP-0003 T01 retains the bounded Zulip caller/transport/test follow-up as an unresolved +navigation applicability question. Broadening does not require declaring that question +resolved; preserve its limitations and keep the authored navigation fixture paused. +WP-0002 T03 retains independent seed/usefulness review. M1/M2 and WP-0004 prerequisites +remain unchanged. Classification is a research aid, not a demand to fill every cell. diff --git a/scripts/classification_coverage.py b/scripts/classification_coverage.py new file mode 100644 index 0000000..55af5f3 --- /dev/null +++ b/scripts/classification_coverage.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +"""Render a provisional coverage inventory; refuse stale or invalid mappings.""" +import argparse +from collections import Counter +import hashlib +import json +from pathlib import Path +import re +import subprocess + +ROOT = Path(__file__).resolve().parents[1] +MAPPING = ROOT / 'research/catalog/ClassificationCoverage.json' +REPORT = ROOT / 'research/catalog/ClassificationCoverage.md' +INITIAL = '2a5dabd' +RESPONSIBILITIES = 'address authority projection affordance mutation synchronization reconciliation composition execution'.split() + + +def section_values(text, key): + section = re.search(r'^' + re.escape(key) + r':\n((?:- [^\n]+\n)+)', text, re.M) + if section is None: + raise ValueError('Missing vocabulary section: ' + key) + return re.findall(r'^- ([\w-]+)$', section[1], re.M) + + +def render(): + data = json.loads(MAPPING.read_text()) + vocab = (ROOT / 'canon/vocabulary.yaml').read_text() + families = section_values(vocab, 'families') + scales = section_values(vocab, 'scales') + forces = section_values(vocab, 'forces') + records = data['candidates'] + paths = [r['path'] for r in records] + actual = {str(p.relative_to(ROOT)) for p in (ROOT / 'research/candidates').glob('*.md')} + if len(set(paths)) != len(paths) or set(paths) != actual: + raise ValueError('Classification must cover every candidate exactly once') + counts = {k: Counter() for k in ['families', 'scales', 'responsibilities', 'improves', 'worsens']} + for r in records: + raw = (ROOT / r['path']).read_bytes() + if hashlib.sha256(raw).hexdigest() != r['sha256']: + raise ValueError('Review classification after candidate changed: ' + r['path']) + front = raw.decode().split('---')[1] + if re.search(r'^title: (.*)$', front, re.M)[1] != r['title'] or not r['rationale'].strip(): + raise ValueError('Invalid title/rationale: ' + r['path']) + for key, allowed in [('families', families), ('scales', scales)]: + labels = [s.strip() for s in re.search(r'^' + key + r': \[(.*)\]$', front, re.M)[1].split(',')] + if len(labels) != len(set(labels)) or not set(labels) <= set(allowed): + raise ValueError('Invalid ' + key) + counts[key].update(labels) + for key, labels, allowed in [('responsibilities', r['responsibilities'], RESPONSIBILITIES), + ('improves', r['forces']['improves'], forces), + ('worsens', r['forces']['worsens'], forces)]: + if not labels or len(labels) != len(set(labels)) or not set(labels) <= set(allowed): + raise ValueError('Invalid classification ' + key + ': ' + r['path']) + counts[key].update(labels) + initial = subprocess.check_output(['git', 'show', INITIAL + ':research/catalog/PatternCandidateCatalog.md'], cwd=ROOT, text=True) + seed_rows = [[c.strip() for c in line.strip('|').split('|')] for line in initial.splitlines() if '| seed |' in line] + original_family = Counter(r[1] for r in seed_rows) + original_scale = Counter(s for r in seed_rows for s in r[2].split('/')) + developed = {r['title'].casefold() for r in records} + aliases = {'URL as Application State': 'URL-addressable State'} + covered = [r[0] for r in seed_rows if aliases.get(r[0], r[0]).casefold() in developed] + uncovered = [r[0] for r in seed_rows if r[0] not in covered] + lines = ['# Classification coverage', '', 'Generated by `python3 scripts/classification_coverage.py`; assessment: ' + data['assessed_at'] + '.', '', + f"{len(records)} developed drafts; {len(seed_rows)} original seeds at `{INITIAL}`. {len(covered)} original seeds developed, {len(uncovered)} without dedicated drafts, {len(records)-len(covered)} later additions. All current candidates remain hypotheses; classification confers no admission or new evidence.", '', + 'Families/scales come from current candidate metadata. Responsibilities and forces are provisional author-model interpretations in [ClassificationCoverage.json](ClassificationCoverage.json), with candidate hashes and per-record rationale. They are separate from canonical metadata.', '', + data['counting_rule'], '', 'A zero means no assigned direct coverage, not absence of discussion. Force direction describes an intended favorable effect or accepted cost, not a measured result. A classification refresh requires reading changed candidates; do not merely replace hashes.', '', + '## Families', '', '| Family | Original seeds | Developed drafts |', '| --- | ---: | ---: |'] + lines += [f'| {k} | {original_family[k]} | {counts["families"][k]} |' for k in families] + lines += ['', '## Scales', '', '| Scale | Original seeds | Developed drafts |', '| --- | ---: | ---: |'] + lines += [f'| {k} | {original_scale[k]} | {counts["scales"][k]} |' for k in scales + ['cross-scale']] + lines += ['', '## Responsibilities', '', 'These are direct-mechanism assignments, not family aliases. In particular, reconciliation is a responsibility and quality is a family.', '', '| Responsibility | Drafts |', '| --- | ---: |'] + lines += [f'| {k} | {counts["responsibilities"][k]} |' for k in RESPONSIBILITIES] + lines += ['', '## Forces', '', 'Selected trade-offs only. Counts are unique candidates per force, even if a force appears on both sides. Unassigned forces remain research questions; the mapping does not certify a benefit.', '', '| Force | Intended benefit | Cost / tension | Distinct drafts |', '| --- | ---: | ---: | ---: |'] + for k in forces: + n = sum(k in r['forces']['improves'] or k in r['forces']['worsens'] for r in records) + lines.append(f'| {k} | {counts["improves"][k]} | {counts["worsens"][k]} | {n} |') + lines += ['', '## Per-candidate responsibility and force rationale', '', '| Candidate | Responsibilities | Intended benefit | Cost / tension | Rationale |', '| --- | --- | --- | --- | --- |'] + for r in records: + lines.append('| [' + r['title'] + '](' + '../candidates/' + Path(r['path']).name + ') | ' + ', '.join(r['responsibilities']) + ' | ' + ', '.join(r['forces']['improves']) + ' | ' + ', '.join(r['forces']['worsens']) + ' | ' + r['rationale'] + ' |') + lines += ['', '## Original seeds without dedicated drafts', ''] + lines += ['- ' + s for s in uncovered] + lines += ['', '## Research implication', '', + 'The developed corpus concentrates on interaction/recovery; authority placement and basic projection choices remain underdeveloped. Family membership alone hides this gap. The next bounded authority/projection comparison is specified in [the research brief](../plan/AuthorityProjectionComparison.md). Keep the navigation fixture paused; pending Zulip applicability questions remain in WP-0003 T01.', '', + 'This inventory is a coverage aid, not a maturity score, empirical learning cycle, graph export or proof that the initial ontology is sufficient.', ''] + return '\n'.join(lines) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--check', action='store_true') + args = parser.parse_args() + content = render() + if args.check: + if not REPORT.exists() or REPORT.read_text() != content: + raise SystemExit('Classification report is stale; review mappings and regenerate') + print('Candidate coverage, hashes, vocabulary and generated report are consistent.') + else: + REPORT.write_text(content) + print('Wrote ' + str(REPORT.relative_to(ROOT))) + + +if __name__ == '__main__': + main() diff --git a/workplans/FEP-WP-0003-contrasting-schools.md b/workplans/FEP-WP-0003-contrasting-schools.md index 2b4fbeb..1b0b2a1 100644 --- a/workplans/FEP-WP-0003-contrasting-schools.md +++ b/workplans/FEP-WP-0003-contrasting-schools.md @@ -251,3 +251,20 @@ defect. T02 retains independent human/AT and offline-case obligations. WP-0006 T retains remaining review dates and comparable-window work; this static protocol is not pooled with runtime cycles. No M1/M2 closure. See research/reviews/ZulipNavigationSourceStudy.md and ZulipNavigationSources.json. + +## Systematic classification and next breadth slice — 2026-09-27 + +T01: ClassificationCoverage.json records provisional direct-responsibility and +selected force trade-offs for all seventeen candidate drafts, with hashes and +rationales. ClassificationCoverage.md is generated and checked against current +candidate families/scales and the original sixteen-seed inventory. Seven original +seeds have developed drafts; nine lack dedicated drafts. No new pattern or evidence +promotion; assignments are author-model interpretations, not full-schema admission. + +The next breadth slice is [AuthorityProjectionComparison](../research/plan/AuthorityProjectionComparison.md): +preregister a bounded six-seed authority/projection comparison, distinguish context +choices from patterns, and produce at most two justified drafts. Registration and +execution remain pending; zero new drafts is a valid outcome. The earlier Zulip +caller/transport/test question remains open with its recorded limitations; no fixture +restart. WP-0002 T03 independent validation, T02/T03 gates here, and WP-0006 comparable +measurement remain unchanged. Do not treat classification counts as maturity.