diff --git a/ascii_bicorder.py b/ascii_bicorder.py index 0170ee2..26cd85e 100755 --- a/ascii_bicorder.py +++ b/ascii_bicorder.py @@ -6,93 +6,9 @@ Generate bicorder.txt from bicorder.json import json import argparse import sys -import os from pathlib import Path -# Simple version-based approach -# -# The model includes a 'bicorder_version' field indicating which version of -# bicorder.json it was trained on. The code checks that versions match before -# calculating. This ensures the gradient structure is compatible. -# -# When bicorder.json changes (gradients added/removed/reordered), update the -# version number and retrain the model. - - -def load_classifier_model(): - """Load the LDA model from bicorder_model.json""" - # Try to find the model file - script_dir = Path(__file__).parent - model_paths = [ - script_dir / 'analysis' / 'bicorder_model.json', - script_dir / 'bicorder_model.json', - Path('analysis/bicorder_model.json'), - Path('bicorder_model.json'), - ] - - for path in model_paths: - if path.exists(): - with open(path, 'r') as f: - return json.load(f) - - return None - - -def calculate_lda_score(values_array, model): - """ - Calculate LDA score from an array of values using the model. - - Args: - values_array: list of 23 values (1-9) in the order expected by the model - model: loaded classifier model - - Returns: - LDA score (float), or None if insufficient data - """ - if model is None: - return None - - if len(values_array) != len(model['dimensions']): - return None - - # Standardize using model scaler - mean = model['scaler']['mean'] - scale = model['scaler']['scale'] - scaled = [(values_array[i] - mean[i]) / scale[i] for i in range(len(values_array))] - - # Calculate LDA score: coef · x + intercept - coef = model['lda']['coefficients'] - intercept = model['lda']['intercept'] - - # Dot product - lda_score = sum(coef[i] * scaled[i] for i in range(len(scaled))) + intercept - - return lda_score - - -def lda_score_to_scale(lda_score): - """ - Convert LDA score to 1-9 scale. - LDA scores typically range from -4 to +4 (8 range) - Target scale is 1 to 9 (8 range) - - Formula: value = 5 + (lda_score * 4/3) - - LDA -3 or less → 1 (bureaucratic) - - LDA 0 → 5 (boundary) - - LDA +3 or more → 9 (relational) - """ - if lda_score is None: - return None - - # Scale: value = 5 + (lda_score * 1.33) - value = 5 + (lda_score * 4.0 / 3.0) - - # Clamp to 1-9 range and round - value = max(1, min(9, value)) - return round(value) - - def calculate_hardness(diagnostic_values): """Calculate hardness/softness (mean of all diagnostic values)""" if not diagnostic_values: @@ -133,34 +49,24 @@ def calculate_automated_analysis(json_data): """ Calculate values for automated analysis fields. Modifies json_data in place. + + Note: the formal/informal (LDA classifier) analysis was removed from the + bicorder in v1.3.0. The cluster classification lives on as research in + analysis/ (see scripts/bicorder_classifier.py). """ # Collect all diagnostic values in order diagnostic_values = [] - values_array = [] for diagnostic_set in json_data.get("diagnostic", []): for gradient in diagnostic_set.get("gradients", []): value = gradient.get("value") if value is not None: diagnostic_values.append(value) - values_array.append(float(value)) - else: - # Fill missing with neutral value - values_array.append(5.0) # Only calculate if we have diagnostic values if not diagnostic_values: return - # Load classifier model - model = load_classifier_model() - - # Check version compatibility - bicorder_version = json_data.get("version", "unknown") - model_version = model.get("bicorder_version", "unknown") if model else "unknown" - - version_mismatch = (model and bicorder_version != model_version) - # Calculate each automated analysis field for analysis_item in json_data.get("analysis", []): if not analysis_item.get("automated", False): @@ -173,14 +79,6 @@ def calculate_automated_analysis(json_data): analysis_item["value"] = calculate_hardness(diagnostic_values) elif term_left == "polarized": analysis_item["value"] = calculate_polarization(diagnostic_values) - elif term_left == "bureaucratic": - if version_mismatch: - # Skip calculation if versions don't match - print(f"Warning: Model version ({model_version}) doesn't match bicorder version ({bicorder_version}). Skipping bureaucratic/relational calculation.") - analysis_item["value"] = None - elif model: - lda_score = calculate_lda_score(values_array, model) - analysis_item["value"] = lda_score_to_scale(lda_score) def center_text(text, width): diff --git a/bicorder-app/src/App.svelte b/bicorder-app/src/App.svelte index 4835d24..92d9968 100644 --- a/bicorder-app/src/App.svelte +++ b/bicorder-app/src/App.svelte @@ -6,18 +6,12 @@ import AnalysisDisplay from './components/AnalysisDisplay.svelte'; import ExportControls from './components/ExportControls.svelte'; import HelpModal from './components/HelpModal.svelte'; - import FormRecommendation from './components/FormRecommendation.svelte'; import AnalysisTransitionBanner from './components/AnalysisTransitionBanner.svelte'; import HamburgerMenu from './components/HamburgerMenu.svelte'; import Landing from './components/Landing.svelte'; - import { BicorderClassifier } from './bicorder-classifier'; // Load bicorder data and model from build-time constants let data: BicorderState = JSON.parse(JSON.stringify(__BICORDER_DATA__)); - const model = __BICORDER_MODEL__; - - // Initialize classifier - const classifier = new BicorderClassifier(model, data.version); // Initialize timestamp if not set if (!data.metadata.timestamp) { @@ -87,10 +81,12 @@ }); // Analysis screens (shown in both shortform and longform) - // Show the useful gradient first (index 3), then the others - const analysisOrder = [3, 0, 1, 2]; // useful, hardness, polarization, formal/informal + // Show the useful gradient first, then the automated ones + const analysisOrder = [2, 0, 1]; // useful, hardness, polarization analysisOrder.forEach((index) => { - screens.push({ type: 'analysis', index, gradient: data.analysis[index] }); + if (index < data.analysis.length) { + screens.push({ type: 'analysis', index, gradient: data.analysis[index] }); + } }); // Export screen @@ -180,57 +176,6 @@ .flatMap(set => set.gradients) .filter(g => !data.metadata.shortform || g.shortform).length; - // Calculate form recommendation (shared by FormRecommendation and AnalysisTransitionBanner) - let formRecommendation: any = null; - let hasEnoughDataForRecommendation = false; - - $: { - // Collect ratings from diagnostic data - const ratings: Record = {}; - let valueCount = 0; - let shortFormTotal = 0; - - data.diagnostic.forEach((diagnosticSet) => { - const setName = diagnosticSet.set_name; - diagnosticSet.gradients.forEach((gradient) => { - // Count shortform gradients - if (gradient.shortform) { - shortFormTotal++; - } - - if (gradient.value !== null) { - const dimensionName = `${setName}_${gradient.term_left}_vs_${gradient.term_right}`; - ratings[dimensionName] = gradient.value; - - // Only count shortform values for the threshold - if (gradient.shortform) { - valueCount++; - } - } - }); - }); - - // Only calculate if at least half of shortform gradients are complete - const threshold = Math.ceil(shortFormTotal / 2); - hasEnoughDataForRecommendation = valueCount >= threshold; - - if (hasEnoughDataForRecommendation && data.metadata.shortform) { - try { - const prediction = classifier.predict(ratings, { detailed: true }); - const assessment = classifier.assessShortFormReadiness(ratings); - formRecommendation = { - ...prediction, - ...assessment, - }; - } catch (error) { - console.error('Error calculating form recommendation:', error); - formRecommendation = null; - } - } else { - formRecommendation = null; - } - } - // Load saved state from localStorage onMount(() => { const saved = localStorage.getItem('bicorder-state'); @@ -328,79 +273,21 @@ return Math.round(Math.max(1, Math.min(9, polarizationScore))); } - function ldaScoreToScale(ldaScore: number | null): number | null { - /** - * Convert LDA score to the analysis[2] "formal vs informal" 1-9 scale. - * LDA scores typically range from -4 to +4 (8 range); target is 1-9. - * - * The model's sign convention (see analysis/bicorder_model.json): - * positive LDA → cluster 2 = Institutional/Bureaucratic = "formal" - * negative LDA → cluster 1 = Relational/Cultural = "informal" - * bicorder.json defines this gradient as 1 = formal, 9 = informal, so a - * positive LDA score must map toward 1 (formal). The score is therefore - * subtracted, not added. - * - * Formula: value = 5 - (ldaScore * 4/3) - * - LDA +3 or more → 1 (formal / institutional / bureaucratic) - * - LDA 0 → 5 (boundary, characteristics of both families) - * - LDA -3 or less → 9 (informal / relational / cultural) - */ - if (ldaScore === null) return null; - - const value = 5 - (ldaScore * 4.0 / 3.0); - - // Clamp to 1-9 range and round - return Math.round(Math.max(1, Math.min(9, value))); - } - - function calculateFormalInformal(): number | null { - // Collect all diagnostic gradients with their set and gradient info - const ratings: Record = {}; - - data.diagnostic.forEach((diagnosticSet) => { - const setName = diagnosticSet.set_name; - diagnosticSet.gradients.forEach((gradient) => { - if (gradient.value !== null) { - // Dimension name must match the model's keys: SetName_left_vs_right - const dimensionName = `${setName}_${gradient.term_left}_vs_${gradient.term_right}`; - ratings[dimensionName] = gradient.value; - } - }); - }); - - // Check if we have any ratings - if (Object.keys(ratings).length === 0) return null; - - try { - // Get prediction from classifier (need detailed: true to get ldaScore) - const result = classifier.predict(ratings, { detailed: true }); - - // Convert LDA score to the 1-9 formal/informal scale - return ldaScoreToScale(result.ldaScore); - } catch (error) { - console.error('Error calculating formal/informal score:', error); - return null; - } - } - // Update automated analysis values reactively + // Note: the formal/informal (LDA classifier) analysis was removed from the + // bicorder in v1.3.0. Cluster classification lives on as research in + // analysis/ (see scripts/bicorder_classifier.py). $: { data.analysis.forEach((item, index) => { if (item.automated) { - if (index === 0) { - // Hardness/Softness - data.analysis[0].value = calculateHardness(); - } else if (index === 1) { - // Polarized/Centrist - data.analysis[1].value = calculatePolarization(); - } else if (index === 2) { - // Formal/Informal (LDA classifier) - data.analysis[2].value = calculateFormalInformal(); + if (item.term_left === 'hardness') { + item.value = calculateHardness(); + } else if (item.term_left === 'polarized') { + item.value = calculatePolarization(); } } }); } - function handleMetadataUpdate(event: CustomEvent) { // Properly trigger reactivity for nested metadata changes data = { @@ -604,7 +491,6 @@ {#if isFirstAnalysisScreen} { - if (val === null || val === undefined) return null; - return (val - this.model.scaler.mean[i]) / this.model.scaler.scale[i]; - }); - } - - /** - * Calculate LDA score (position on discriminant axis) - * @private - */ - _ldaScore(scaledValues) { - // Fill missing values with 0 (mean in scaled space) - const filled = scaledValues.map(v => v === null ? 0 : v); - - // Calculate: coef · x + intercept - let score = this.model.lda.intercept; - for (let i = 0; i < filled.length; i++) { - score += this.model.lda.coefficients[i] * filled[i]; - } - return score; - } - - /** - * Calculate Euclidean distance - * @private - */ - _distance(a, b) { - let sum = 0; - for (let i = 0; i < a.length; i++) { - const diff = a[i] - b[i]; - sum += diff * diff; - } - return Math.sqrt(sum); - } - - /** - * Predict cluster for given ratings - * - * @param {Object} ratings - Map of dimension names to values (1-9) - * Can be partial - missing dimensions handled gracefully - * @param {Object} options - Options - * @param {boolean} options.detailed - Return detailed information (default: true) - * - * @returns {Object} Prediction result with: - * - cluster: Cluster number (1 or 2) - * - clusterName: Human-readable name - * - confidence: Confidence percentage (0-100) - * - completeness: Percentage of dimensions provided (0-100) - * - recommendedForm: 'short' or 'long' - * - ldaScore: Position on discriminant axis - * - distanceToBoundary: Distance from cluster boundary - */ - predict(ratings, options = { detailed: true }) { - // Convert ratings object to array - const values = this.dimensions.map(dim => ratings[dim] ?? null); - const providedCount = values.filter(v => v !== null).length; - const completeness = providedCount / this.dimensions.length; - - // Fill missing with neutral value (5 = middle of 1-9 scale) - const filled = values.map(v => v ?? 5); - - // Standardize - const scaled = this._standardize(filled); - - // Calculate LDA score - const ldaScore = this._ldaScore(scaled); - - // Predict cluster (LDA boundary at 0) - // Positive score = cluster 2 (Institutional) - // Negative score = cluster 1 (Relational) - const cluster = ldaScore > 0 ? 2 : 1; - const clusterName = this.model.cluster_names[cluster]; - - // Calculate confidence based on distance from boundary - const distanceToBoundary = Math.abs(ldaScore); - - // Confidence: higher when further from boundary - // Normalize based on typical strong separation (3.0) - let confidence = Math.min(1.0, distanceToBoundary / 3.0); - - // Adjust for completeness - const adjustedConfidence = confidence * (0.5 + 0.5 * completeness); - - // Recommend form - // Use long form when multiple issues are present: - // 1. Low confidence (< 0.5) - // 2. Low completeness (< 50% of dimensions) - // 3. Near boundary (< 0.3 distance) - // Require at least 2 conditions to be true - const issues = [ - adjustedConfidence < this.model.thresholds.confidence_low, - completeness < this.model.thresholds.completeness_low, - distanceToBoundary < this.model.thresholds.boundary_distance_low - ]; - const issueCount = issues.filter(Boolean).length; - const shouldUseLongForm = issueCount >= 2; - - const recommendedForm = shouldUseLongForm ? 'long' : 'short'; - - const basicResult = { - cluster, - clusterName, - confidence: Math.round(adjustedConfidence * 100), - completeness: Math.round(completeness * 100), - recommendedForm, - }; - - if (!options.detailed) { - return basicResult; - } - - // Calculate distances to cluster centroids - const filledScaled = scaled.map(v => v ?? 0); - const distances = {}; - for (const [clusterId, centroid] of Object.entries(this.model.cluster_centroids_scaled)) { - distances[clusterId] = this._distance(filledScaled, centroid); - } - - // Count key dimensions provided - const keyDimensionsProvided = this.keyDimensions.filter( - dim => ratings[dim] !== null && ratings[dim] !== undefined - ).length; - - return { - ...basicResult, - ldaScore, - distanceToBoundary, - dimensionsProvided: providedCount, - dimensionsTotal: this.dimensions.length, - keyDimensionsProvided, - keyDimensionsTotal: this.keyDimensions.length, - distancesToCentroids: distances, - rawConfidence: Math.round(confidence * 100), - }; - } - - /** - * Get explanation of classification - * - * @param {Object} ratings - Dimension ratings - * @returns {string} Human-readable explanation - */ - explainClassification(ratings) { - const result = this.predict(ratings, { detailed: true }); - const lines = []; - - lines.push(`Protocol Classification: ${result.clusterName}`); - lines.push(`Confidence: ${result.confidence}%`); - lines.push(''); - - if (result.cluster === 2) { - lines.push('This protocol leans toward Institutional/Bureaucratic characteristics:'); - lines.push(' • More likely to be formal, standardized, top-down'); - lines.push(' • May involve state/corporate enforcement'); - lines.push(' • Tends toward precise, documented procedures'); - } else { - lines.push('This protocol leans toward Relational/Cultural characteristics:'); - lines.push(' • More likely to be emergent, community-based'); - lines.push(' • May involve voluntary participation'); - lines.push(' • Tends toward interpretive, flexible practices'); - } - - lines.push(''); - lines.push(`Distance from boundary: ${result.distanceToBoundary.toFixed(2)}`); - - if (result.distanceToBoundary < 0.5) { - lines.push('⚠️ This protocol is near the boundary between families.'); - lines.push(' It may exhibit characteristics of both types.'); - } - - lines.push(''); - lines.push(`Completeness: ${result.completeness}% (${result.dimensionsProvided}/${result.dimensionsTotal} dimensions)`); - - if (result.completeness < 100) { - lines.push('Note: Missing dimensions filled with neutral values (5)'); - lines.push(' Confidence improves with complete data'); - } - - lines.push(''); - lines.push(`Recommended form: ${result.recommendedForm.toUpperCase()}`); - - if (result.recommendedForm === 'long') { - lines.push('Reason: Use long form for:'); - if (result.confidence < 60) { - lines.push(' • Low classification confidence'); - } - if (result.completeness < 50) { - lines.push(' • Incomplete data'); - } - if (result.distanceToBoundary < 0.5) { - lines.push(' • Ambiguous positioning between families'); - } - } else { - lines.push(`Reason: High confidence classification with ${result.completeness}% data`); - } - - return lines.join('\n'); - } - - /** - * Get the list of key dimensions for short form - * @returns {Array} Dimension names - */ - getKeyDimensions() { - return [...this.keyDimensions]; - } - - /** - * Check if enough key dimensions are provided for reliable short-form classification - * @param {Object} ratings - Current ratings - * @returns {Object} Assessment with recommendation - */ - assessShortFormReadiness(ratings) { - const keyProvided = this.keyDimensions.filter( - dim => ratings[dim] !== null && ratings[dim] !== undefined - ); - - const coverage = keyProvided.length / this.keyDimensions.length; - const isReady = coverage >= 0.75; // 75% of key dimensions - - return { - ready: isReady, - keyDimensionsProvided: keyProvided.length, - keyDimensionsTotal: this.keyDimensions.length, - coverage: Math.round(coverage * 100), - missingKeyDimensions: this.keyDimensions.filter( - dim => !ratings[dim] - ), - }; - } -} - diff --git a/bicorder-app/src/components/AnalysisTransitionBanner.svelte b/bicorder-app/src/components/AnalysisTransitionBanner.svelte index a596d15..921d7a8 100644 --- a/bicorder-app/src/components/AnalysisTransitionBanner.svelte +++ b/bicorder-app/src/components/AnalysisTransitionBanner.svelte @@ -3,7 +3,6 @@ import AnalysisDisplay from './AnalysisDisplay.svelte'; import type { AnalysisGradient } from '../types'; - export let recommendation: any = null; export let isShortForm: boolean; export let completedGradients: number; export let allAnalysisGradients: AnalysisGradient[]; @@ -15,8 +14,6 @@ updateAnalysisNotes: { index: number; notes: string }; }>(); - $: hasRecommendation = recommendation?.recommendedForm === 'long'; - let showAllAnalysis = false; function handleSwitchToLongForm() { @@ -42,32 +39,6 @@ - - {#if isShortForm && hasRecommendation && recommendation} -
-
- ⚠ - Long Form Recommended -
-
-

- {#if recommendation.confidence < 60} - • Low classification confidence ({recommendation.confidence}%)
- {/if} - {#if recommendation.completeness < 50} - • Incomplete data ({recommendation.completeness}% of dimensions)
- {/if} - {#if recommendation.distanceToBoundary < 0.5} - • Protocol near boundary between families
- {/if} - {#if recommendation.coverage < 75} - • Missing key dimensions for reliable short-form classification ({recommendation.coverage}% coverage)
- {/if} -

-
-
- {/if} -
- - {#if isExpanded} -
{}} role="button" tabindex="-1"> -
{}} role="dialog" aria-modal="true"> -
-

Data Quality Assessment

- -
- -
-
- Classification Confidence: - - {recommendation.confidence}% - -
- -
- Data Completeness: - - {recommendation.completeness}% ({recommendation.dimensionsProvided}/{recommendation.dimensionsTotal} dimensions) - -
- -
- Key Dimensions: - - {recommendation.coverage}% ({recommendation.keyDimensionsProvided}/{recommendation.keyDimensionsTotal}) - -
- -
-
Current Classification:
-
- {recommendation.clusterName} - {#if recommendation.distanceToBoundary < 0.5} - (Near boundary) - {/if} -
-
- - {#if recommendation.recommendedForm === 'long'} -
- ⚠ Long Form Recommended -

- {#if recommendation.confidence < 60} - • Low classification confidence
- {/if} - {#if recommendation.completeness < 50} - • Incomplete data (less than 50% of dimensions)
- {/if} - {#if recommendation.distanceToBoundary < 0.5} - • Protocol near boundary between families
- {/if} - {#if recommendation.coverage < 75} - • Missing key dimensions for reliable short-form classification
- {/if} -

- -

Returns to the beginning. All your current values will be preserved.

-
- {:else} -
- ✓ Short Form Working Well -

- Your current data provides {recommendation.confidence}% confidence classification. - Continue with short form or switch to long form for more detailed analysis. -

-
- {/if} -
-
-
- {/if} -
-{/if} - - diff --git a/bicorder-app/src/vite-env.d.ts b/bicorder-app/src/vite-env.d.ts index bcf8be3..6e3eca6 100644 --- a/bicorder-app/src/vite-env.d.ts +++ b/bicorder-app/src/vite-env.d.ts @@ -2,7 +2,6 @@ /// declare const __BICORDER_DATA__: any -declare const __BICORDER_MODEL__: any interface ImportMetaEnv { readonly VITE_APP_TITLE: string diff --git a/bicorder-app/vite.config.ts b/bicorder-app/vite.config.ts index cdf4ca6..2bd71b2 100644 --- a/bicorder-app/vite.config.ts +++ b/bicorder-app/vite.config.ts @@ -9,11 +9,6 @@ const bicorderData = JSON.parse( fs.readFileSync(path.resolve(__dirname, '../bicorder.json'), 'utf-8') ) -// Read bicorder_model.json at build time -const bicorderModel = JSON.parse( - fs.readFileSync(path.resolve(__dirname, '../analysis/bicorder_model.json'), 'utf-8') -) - export default defineConfig({ base: './', plugins: [ @@ -61,7 +56,6 @@ export default defineConfig({ }) ], define: { - '__BICORDER_DATA__': JSON.stringify(bicorderData), - '__BICORDER_MODEL__': JSON.stringify(bicorderModel) + '__BICORDER_DATA__': JSON.stringify(bicorderData) } }) diff --git a/bicorder.json b/bicorder.json index 3f162f9..5e86b55 100644 --- a/bicorder.json +++ b/bicorder.json @@ -1,20 +1,18 @@ { "name": "Protocol Bicorder", "schema": "bicorder.schema.json", - "version": "1.2.6", + "version": "1.3.0", "description": "A diagnostic tool for the study of protocols", "author": "Nathan Schneider", - "date_modified": "2026-02-21", - + "date_modified": "2026-09-22", "metadata": { - "protocol": null, - "description": null, - "analyst": null, - "standpoint": null, - "timestamp": null, - "shortform": true + "protocol": null, + "description": null, + "analyst": null, + "standpoint": null, + "timestamp": null, + "shortform": true }, - "diagnostic": [ { "set_name": "Design", @@ -242,47 +240,36 @@ ] } ], - "analysis": [ - { - "term_left": "hardness", - "term_left_description": "The protocol tends toward properties characterized by hardness", - "term_right": "softness", - "term_right_description": "The protocol tends toward properties characterized by softness", - "instructions": "Take all the 'value' fields in the gradients above and determine a mean. Round it to the nearest integer. That is the 'value' here.", - "automated": true, - "value": null, - "notes": null - }, - { - "term_left": "polarized", - "term_left_description": "The analyst tended toward more extreme high or low readings", - "term_right": "centrist", - "term_right_description": "The analyst tended toward readings at the middle of the gradients", - "instructions": "Take all the 'value' fields in the gradients above. Assess their degree of polarization. For instance, if all the values are either 1 or 9, the output would be 1, and if all of them are 5, the output would be 9.", - "automated": true, - "value": null, - "notes": null - }, - { - "term_left": "formal", - "term_left_description": "Exhibits bureaucratic characteristics with centralized control and predictable enforcement", - "term_right": "informal", - "term_right_description": "Exhibits relational characteristics with distributed coordination embedded in culture", - "instructions": "Based on the diagnostic readings, calculate the protocol's position using Linear Discriminant Analysis. The LDA score is scaled to the 1-9 range, where 1 represents strongly formal protocols and 9 represents strongly informal protocols. A score of 5 indicates a protocol near the boundary exhibiting characteristics of both families.", - "automated": true, - "value": null, - "notes": null - }, - { - "term_left": "not useful", - "term_left_description": "The bicorder was not useful or relevant for analyzing this protocol", - "term_right": "very useful", - "term_right_description": "The bicorder was very useful and relevant for analyzing this protocol", - "instructions": "Evaluate the usefulness of this bicorder as a tool for analyzing this protocol, considering whether the gradient terms seemed revealing or irrelevant.", - "automated": false, - "value": null, - "notes": null - } + { + "term_left": "hardness", + "term_left_description": "The protocol tends toward properties characterized by hardness", + "term_right": "softness", + "term_right_description": "The protocol tends toward properties characterized by softness", + "instructions": "Take all the 'value' fields in the gradients above and determine a mean. Round it to the nearest integer. That is the 'value' here.", + "automated": true, + "value": null, + "notes": null + }, + { + "term_left": "polarized", + "term_left_description": "The analyst tended toward more extreme high or low readings", + "term_right": "centrist", + "term_right_description": "The analyst tended toward readings at the middle of the gradients", + "instructions": "Take all the 'value' fields in the gradients above. Assess their degree of polarization. For instance, if all the values are either 1 or 9, the output would be 1, and if all of them are 5, the output would be 9.", + "automated": true, + "value": null, + "notes": null + }, + { + "term_left": "not useful", + "term_left_description": "The bicorder was not useful or relevant for analyzing this protocol", + "term_right": "very useful", + "term_right_description": "The bicorder was very useful and relevant for analyzing this protocol", + "instructions": "Evaluate the usefulness of this bicorder as a tool for analyzing this protocol, considering whether the gradient terms seemed revealing or irrelevant.", + "automated": false, + "value": null, + "notes": null + } ] } diff --git a/bicorder.txt b/bicorder.txt index edd0f88..32072c3 100644 --- a/bicorder.txt +++ b/bicorder.txt @@ -39,7 +39,6 @@ self-enforcing < [---------] > enforced ANALYSIS hardness < [---------] > softness polarized < [---------] > centrist - formal < [---------] > informal not useful < [---------] > very useful GLOSSARY @@ -64,12 +63,10 @@ self-enforcing < [---------] > enforced | explicit | Design is stated explicitly somewhere that is accessible to participants | | exposed | Weak boundaries and vulnerable to external influence | | flocking | Coordination occurs through centralized direction or direct mimicry | -| formal | Exhibits bureaucratic characteristics with centralized control and predictable enforcement | | hardness | The protocol tends toward properties characterized by hardness | | implicit | Design is not stated explicitly and is learned by use | | inclusion | Reduces barriers and includes diverse participants | -| informal | Exhibits relational characteristics with distributed coordination embedded in culture | -| institutional | Design occurs through processes controlled by powerful institutions | +| institutional | Design occurs through processes controlled by particular institutions | | interpretive | Ambiguous design, allowing participants a wide range of interpretation | | liberating | Enables participants to carry out desired activities with less work or thought | | limited | Does not, on its own, adequately meet the needs and goals of participants |