From 3157645a4e180d063c1df9d2dc8d403b17652541 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 21:15:06 +0900 Subject: [PATCH 01/12] feat(ontology): publish DOT/FJA worker-function taxonomy Port Functional Job Analysis Data/People/Things worker functions into the published semantic layer: 24 concepts with official DOT Appendix B definitions verbatim, definitional ordinal ranks, and qualitative cognitive/affective/behavioral facet tags drawn only from published Fleishman ability and O*NET skill vocabularies (ADR 0232). Add a deterministic fail-closed application read model, tests pinning completeness/verbatim text/closed facet vocabulary, doctoring citation registry, PRD-FR-2A, CHANGELOG, and version bump to 2.19.0. --- CHANGELOG.md | 8 + ...tion-taxonomy-in-the-published-ontology.md | 118 +++++++++ docs/adr/README.md | 1 + .../WORKER_FUNCTION_TAXONOMY_REFERENCES.md | 49 ++++ docs/ontology/lineageweave-kg.ttl | 239 +++++++++++++++++ docs/product-requirements.md | 17 ++ lineageweave/worker_function_taxonomy.py | 222 ++++++++++++++++ pyproject.toml | 2 +- tests/test_worker_function_taxonomy.py | 247 ++++++++++++++++++ uv.lock | 2 +- 10 files changed, 903 insertions(+), 2 deletions(-) create mode 100644 docs/adr/0232-worker-function-taxonomy-in-the-published-ontology.md create mode 100644 docs/doctoring/WORKER_FUNCTION_TAXONOMY_REFERENCES.md create mode 100644 lineageweave/worker_function_taxonomy.py create mode 100644 tests/test_worker_function_taxonomy.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 641306055..5f09e7d40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,14 @@ All notable changes to this project are documented here. Format follows ### Added +- The DOT/FJA Data/People/Things worker-function taxonomy is now published + in the canonical ontology: all 24 worker functions carry the official + Dictionary of Occupational Titles Appendix B definitions verbatim, their + definitional ordinal ranks, and qualitative cognitive, affective, and + behavioral facet tags drawn only from published Fleishman ability and + O*NET skill vocabularies (ADR 0232). A deterministic application read + model (`lineageweave.worker_function_taxonomy`) exposes fail-closed + lookups; ranks are scale positions and are never used as weights. - Persist explicit paragraph, list, table, MathML formula, and caller-parsed conversation-turn semantic-unit kinds without inferring absent boundaries. - Event Lineage now persists each reconstructed connection's independent diff --git a/docs/adr/0232-worker-function-taxonomy-in-the-published-ontology.md b/docs/adr/0232-worker-function-taxonomy-in-the-published-ontology.md new file mode 100644 index 000000000..bc6e3529d --- /dev/null +++ b/docs/adr/0232-worker-function-taxonomy-in-the-published-ontology.md @@ -0,0 +1,118 @@ +# ADR 0232: Worker-function taxonomy in the published ontology + +**Status:** Accepted +**Date:** 2026-08-26 +**Extends:** [ADR 0004](0004-knowledge-graph-ontology.md), [ADR 0145](0145-psychometric-channel-weight-estimation.md), [ADR 0207](0207-repository-case-ontology-namespace-canonical.md) + +## Context + +Industrial and organizational psychology describes work through +worker functions rather than job titles. Functional Job Analysis (FJA) +expresses every job's relationship to *Data*, *People*, and *Things* +through three ordered lists, carried verbatim in the Dictionary of +Occupational Titles Appendix B (U.S. Department of Labor, 1991): Data +ranks 0-6, People ranks 0-8, Things ranks 0-7, each ordered so that the +lower digit names the more complex function (Fine & Cronshaw, 1999). +Each function definition exercises identifiable cognitive, +affective/social, and behavioral facets that map onto the published +Fleishman ability taxonomy and O*NET basic and cross-functional skills +(Fleishman & Quaintance, 1984; Fleishman, Costanza, & +Marshall-Mies, 1999; Mumford, Peterson, & Childs, 1999; Peterson et al., +1999). + +The repository had no representation for this vocabulary: a post or +analysis cannot yet say "this evidence describes Analyzing-level data +work" without inventing an untracked term. The ontology is the correct +home -- it already publishes the governed semantic layer over +PostgreSQL graph facts -- but two constraints bind any addition: + +1. The lookup-code round trip (`tests/test_ontology.py`) requires every + `:lookupCode` term to exist as a seeded `common_lookup_value` row and + vice versa. Worker functions are not relational lookup rows today. +2. Measurement stays governed by ADR 0145: nothing may mint a numeric + weight from a qualitative taxonomy. + +## Decision + +1. Publish all 24 worker functions as a `skos:ConceptScheme` + (`:workerFunctionScheme`) of `:WorkerFunction` concepts in + `docs/ontology/lineageweave-kg.ttl`, each carrying the official DOT + Appendix B definition verbatim as its `rdfs:comment`, its definitional + rank (`:fjaRank`), its domain (`:fjaDomain`), and qualitative facet + tags (`:cognitiveFacet` / `:affectiveFacet` / `:behavioralFacet`) + naming only published Fleishman ability families or O*NET skill and + work-style constructs. +2. Like column-projection datatype properties, these concepts carry no + `:lookupCode`: they are not `common_lookup_value` rows, so the round + trip is untouched. Binding a function to stored rows needs a separate + schema-and-seed decision. +3. Ranks are scale positions copied from the published table. They are + never fitted, calibrated, renormalized, or used as weights; this + decision adds zero arithmetic to the measurement layer. +4. The application read model lives in + `lineageweave/worker_function_taxonomy.py`: cached, deterministically + sorted records; `(domain, rank)` lookups that return ``None``/``{}`` + for absent ranks (honest unknown) and raise ``ValueError`` for an + unrecognized domain (caller error); fail-closed ``ValueError`` on a + malformed TTL declaration. +5. The canonical repository-case namespace (ADR 0207) mints every IRI; + no lowercase compatibility form is introduced. + +## Consequences + +- The IO-psychology worker-function vocabulary becomes addressable and + citable inside the published semantic layer before any persistence + decision exists. +- Facet tags are an editorial projection of official definitions onto + published taxonomies. They carry no weight and make no calibration + claim; presenting them as measured requirements would violate the + measurement boundary. +- Adding DB-backed function annotations later means one migration plus + seed rows and `:lookupCode` declarations -- the extension path is + additive by construction. +- `tests/test_worker_function_taxonomy.py` pins the published text with + verbatim definition prefixes and closes the facet vocabulary, so + drift toward invented constructs fails CI. + +## Verification + +- `tests/test_worker_function_taxonomy.py`: completeness (24 concepts), + per-domain rank extents, verbatim official-definition prefixes, closed + facet vocabulary, deterministic ordering, canonical namespace, + fail-closed lookups. +- `tests/test_ontology.py` continues to pass unchanged: the round trip + sees no new lookup codes. + +## References + +Fine, S. A., & Cronshaw, S. F. (1999). *Functional job analysis: A +foundation for human resources management*. Lawrence Erlbaum +Associates. + +Fleishman, E. A., & Quaintance, M. K. (1984). *Taxonomies of human +performance: The description of human tasks*. Academic Press. + +Fleishman, E. A., Costanza, D. P., & Marshall-Mies, J. C. (1999). +Abilities. In N. G. Peterson, M. D. Mumford, W. C. Borman, P. R. +Jeanneret, & E. A. Fleishman (Eds.), *An occupational information system +for the 21st century: The development of O*NET* (pp. 97-112). American +Psychological Association. + +Mumford, M. D., Peterson, N. G., & Childs, R. A. (1999). Basic and +cross-functional skills. In N. G. Peterson, M. D. Mumford, W. C. Borman, +P. R. Jeanneret, & E. A. Fleishman (Eds.), *An occupational information +system for the 21st century: The development of O*NET* (pp. 49-69). +American Psychological Association. + +Peterson, N. G., Mumford, M. D., Borman, W. C., Jeanneret, P. R., & +Fleishman, E. A. (Eds.). (1999). *An occupational information system for +the 21st century: The development of O\*NET*. American Psychological +Association. + +U.S. Department of Labor. (1991). *Dictionary of occupational titles* +(4th ed., rev., Appendix B). U.S. Government Printing Office. +https://www.dol.gov/agencies/oalj/PUBLIC/DOT/REFERENCES/DOTAPPB + +Cyganiak, R., Wood, D., & Lanthaler, M. (Eds.). (2014). *RDF 1.1 +concepts and abstract syntax*. World Wide Web Consortium. +https://www.w3.org/TR/rdf11-concepts/ diff --git a/docs/adr/README.md b/docs/adr/README.md index 83e56345c..e92be6b20 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -27,6 +27,7 @@ decision from them. | Evidence operations Dashboard (`/`) | [0206](0206-evidence-operations-dashboard.md) | | [`temporal-topic-context-influence-research.md`](../temporal-topic-context-influence-research.md) | [0210](0210-temporal-topic-context-influence-dashboard.md) | | [`python-mathematical-compute-boundary-audit.md`](../doctoring/python-mathematical-compute-boundary-audit.md) | [0208](0208-externalize-local-mathematical-compute.md) | +| [`WORKER_FUNCTION_TAXONOMY_REFERENCES.md`](../doctoring/WORKER_FUNCTION_TAXONOMY_REFERENCES.md) | [0232](0232-worker-function-taxonomy-in-the-published-ontology.md) | [0011](0011-prov-o-standard-relations.md) and [0065](0065-prov-o-provenance-boundary.md) cite the dated W3C PROV-O and PROV-DM Recommendations (https://www.w3.org/TR/2013/REC-prov-o-20130430/ and https://www.w3.org/TR/2013/REC-prov-dm-20130430/). diff --git a/docs/doctoring/WORKER_FUNCTION_TAXONOMY_REFERENCES.md b/docs/doctoring/WORKER_FUNCTION_TAXONOMY_REFERENCES.md new file mode 100644 index 000000000..9482ecb92 --- /dev/null +++ b/docs/doctoring/WORKER_FUNCTION_TAXONOMY_REFERENCES.md @@ -0,0 +1,49 @@ +# Worker-Function Taxonomy References + +Supporting literature for [ADR +0232](../adr/0232-worker-function-taxonomy-in-the-published-ontology.md). +APA 7th edition. This list is the citation record for the DOT/FJA +Data/People/Things worker functions and their cognitive, affective, and +behavioral facet projections. + +Fine, S. A., & Cronshaw, S. F. (1999). *Functional job analysis: A +foundation for human resources management*. Lawrence Erlbaum Associates. + +Fleishman, E. A., & Quaintance, M. K. (1984). *Taxonomies of human +performance: The description of human tasks*. Academic Press. + +Fleishman, E. A., Costanza, D. P., & Marshall-Mies, J. C. (1999). +Abilities. In N. G. Peterson, M. D. Mumford, W. C. Borman, P. R. +Jeanneret, & E. A. Fleishman (Eds.), *An occupational information system +for the 21st century: The development of O\*NET* (pp. 97–112). American +Psychological Association. + +Mumford, M. D., Peterson, N. G., & Childs, R. A. (1999). Basic and +cross-functional skills. In N. G. Peterson, M. D. Mumford, W. C. Borman, +P. R. Jeanneret, & E. A. Fleishman (Eds.), *An occupational information +system for the 21st century: The development of O\*NET* (pp. 49–69). +American Psychological Association. + +Peterson, N. G., Mumford, M. D., Borman, W. C., Jeanneret, P. R., & +Fleishman, E. A. (Eds.). (1999). *An occupational information system for +the 21st century: The development of O\*NET*. American Psychological +Association. + +U.S. Department of Labor. (1991). *Dictionary of occupational titles* +(4th ed., rev., Appendix B). U.S. Government Printing Office. +https://www.dol.gov/agencies/oalj/PUBLIC/DOT/REFERENCES/DOTAPPB + +Cyganiak, R., Wood, D., & Lanthaler, M. (Eds.). (2014). *RDF 1.1 +concepts and abstract syntax*. World Wide Web Consortium. +https://www.w3.org/TR/rdf11-concepts/ + +Knublauch, H., & Kontokostas, D. (Eds.). (2017). *Shapes constraint +language (SHACL)*. World Wide Web Consortium. https://www.w3.org/TR/shacl/ + +## Scope note + +Worker-function ranks are ordinal positions copied from the published +DOT table; they are definitional scale positions and never fitted or +calibrated weights. Score interpretation and fairness remain governed by +AERA, APA, and NCME (2014), cited in the repository's measurement-boundary +ADRs ([ADR 0145](../adr/0145-psychometric-channel-weight-estimation.md)). diff --git a/docs/ontology/lineageweave-kg.ttl b/docs/ontology/lineageweave-kg.ttl index 0aeb57f9a..0daab5e43 100644 --- a/docs/ontology/lineageweave-kg.ttl +++ b/docs/ontology/lineageweave-kg.ttl @@ -436,3 +436,242 @@ :semanticConfidence a owl:DatatypeProperty ; rdfs:domain :ProjectMention ; rdfs:range xsd:decimal . + +################################################################# +# Worker-function taxonomy (ADR 0232). +# +# The Dictionary of Occupational Titles' Data/People/Things worker +# functions (U.S. Department of Labor, 1991, Appendix B) descend from +# Functional Job Analysis (Fine & Cronshaw, 1999). Each function below +# carries the official DOT definition verbatim as its rdfs:comment, +# its definitional ordinal rank (:fjaRank -- lower digits denote the +# more complex function; these are scale positions, never fitted or +# calibrated weights), and qualitative facet tags naming published +# Fleishman ability families and O*NET basic / cross-functional skills +# (Fleishman et al., 1999; Mumford et al., 1999; Peterson et al., +# 1999). Facet tags are an editorial projection of each official +# definition onto those taxonomies: they carry no numeric weight, no +# calibration, and no measurement claim -- channel-weight estimation +# stays governed by ADR 0145. +# +# Like column-projection properties above, these concepts deliberately +# do NOT carry :lookupCode: they are not common_lookup_value rows, so +# the lookup-code round trip is unaffected. +################################################################# + +:WorkerFunction a owl:Class ; + rdfs:label "Worker function"@en ; + rdfs:comment "One DOT/FJA Data, People, or Things worker function: the standard terminology for how a worker functions on a job in relation to data, people, or things."@en . + +:workerFunctionScheme a skos:ConceptScheme ; + skos:prefLabel "DOT/FJA worker functions"@en ; + skos:definition "The three ordered DOT worker-function lists (Data 0-6, People 0-8, Things 0-7), each arranged from the most complex to the simplest relationship."@en ; + rdfs:seeAlso . + +:fjaDomain a owl:DatatypeProperty ; + rdfs:domain :WorkerFunction ; + rdfs:range xsd:string ; + rdfs:label "FJA domain"@en ; + rdfs:comment "Which DOT list the function belongs to: exactly one of \"data\", \"people\", or \"things\"."@en . + +:fjaRank a owl:DatatypeProperty ; + rdfs:domain :WorkerFunction ; + rdfs:range xsd:integer ; + rdfs:label "FJA rank"@en ; + rdfs:comment "The function's definitional position on its DOT list. Lower digits name the more complex function; the digit is a scale position from the published table, not a fitted weight."@en . + +:cognitiveFacet a owl:AnnotationProperty ; + rdfs:label "cognitive facet"@en ; + rdfs:comment "A named Fleishman ability family or O*NET skill that the official function definition exercises cognitively. Editorial projection only; no weight or calibration."@en . + +:affectiveFacet a owl:AnnotationProperty ; + rdfs:label "affective facet"@en ; + rdfs:comment "A named O*NET work style or social skill that the official function definition exercises affectively. Editorial projection only; no weight or calibration."@en . + +:behavioralFacet a owl:AnnotationProperty ; + rdfs:label "behavioral facet"@en ; + rdfs:comment "A named Fleishman psychomotor/physical ability or O*NET behavioral skill that the official function definition exercises behaviorally. Editorial projection only; no weight or calibration."@en . + +# ---- Data (4th DOT digit): information, knowledge, and conceptions ---- + +:dataSynthesizing a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Synthesizing"@en ; + :fjaDomain "data" ; :fjaRank 0 ; + :cognitiveFacet "Inductive Reasoning" , "Fluency of Ideas" ; + rdfs:comment "Integrating analyses of data to discover facts and/or develop knowledge concepts or interpretations."@en . + +:dataCoordinating a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Coordinating"@en ; + :fjaDomain "data" ; :fjaRank 1 ; + :cognitiveFacet "Time Management" , "Coordination" ; + :behavioralFacet "Oral Expression" ; + rdfs:comment "Determining time, place, and sequence of operations or action to be taken on the basis of analysis of data; executing determinations and/or reporting on events."@en . + +:dataAnalyzing a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Analyzing"@en ; + :fjaDomain "data" ; :fjaRank 2 ; + :cognitiveFacet "Deductive Reasoning" , "Inductive Reasoning" , "Critical Thinking" ; + rdfs:comment "Examining and evaluating data. Presenting alternative actions in relation to the evaluation is frequently involved."@en . + +:dataCompiling a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Compiling"@en ; + :fjaDomain "data" ; :fjaRank 3 ; + :cognitiveFacet "Category Flexibility" , "Written Comprehension" ; + rdfs:comment "Gathering, collating, or classifying information about data, people, or things. Reporting and/or carrying out a prescribed action in relation to the information is frequently involved."@en . + +:dataComputing a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Computing"@en ; + :fjaDomain "data" ; :fjaRank 4 ; + :cognitiveFacet "Number Facility" , "Mathematical Reasoning" ; + rdfs:comment "Performing arithmetic operations and reporting on and/or carrying out a prescribed action in relation to them. Does not include counting."@en . + +:dataCopying a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Copying"@en ; + :fjaDomain "data" ; :fjaRank 5 ; + :cognitiveFacet "Written Comprehension" ; + :behavioralFacet "Finger Dexterity" ; + rdfs:comment "Transcribing, entering, or posting data."@en . + +:dataComparing a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Comparing"@en ; + :fjaDomain "data" ; :fjaRank 6 ; + :cognitiveFacet "Perceptual Speed" ; + rdfs:comment "Judging the readily observable functional, structural, or compositional characteristics (whether similar to or divergent from obvious standards) of data, people, or things."@en . + +# ---- People (5th DOT digit): human beings dealt with individually ---- + +:peopleMentoring a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Mentoring"@en ; + :fjaDomain "people" ; :fjaRank 0 ; + :affectiveFacet "Social Perceptiveness" ; + :behavioralFacet "Instructing" , "Active Listening" ; + rdfs:comment "Dealing with individuals in terms of their total personality in order to advise, counsel, and/or guide them with regard to problems that may be resolved by legal, scientific, clinical, spiritual, and/or other professional principles."@en . + +:peopleNegotiating a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Negotiating"@en ; + :fjaDomain "people" ; :fjaRank 1 ; + :affectiveFacet "Social Perceptiveness" ; + :behavioralFacet "Negotiation" , "Persuasion" ; + rdfs:comment "Exchanging ideas, information, and opinions with others to formulate policies and programs and/or arrive jointly at decisions, conclusions, or solutions."@en . + +:peopleInstructing a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Instructing"@en ; + :fjaDomain "people" ; :fjaRank 2 ; + :behavioralFacet "Instructing" , "Speaking" ; + rdfs:comment "Teaching subject matter to others, or training others (including animals) through explanation, demonstration, and supervised practice; or making recommendations on the basis of technical disciplines."@en . + +:peopleSupervising a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Supervising"@en ; + :fjaDomain "people" ; :fjaRank 3 ; + :behavioralFacet "Management of Personnel Resources" , "Monitoring" , "Coordination" ; + rdfs:comment "Determining or interpreting work procedures for a group of workers, assigning specific duties to them, maintaining harmonious relations among them, and promoting efficiency. A variety of responsibilities is involved in this function."@en . + +:peopleDiverting a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Diverting"@en ; + :fjaDomain "people" ; :fjaRank 4 ; + :behavioralFacet "Speaking" ; + rdfs:comment "Amusing others, usually through the medium of stage, screen, television, or radio."@en . + +:peoplePersuading a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Persuading"@en ; + :fjaDomain "people" ; :fjaRank 5 ; + :behavioralFacet "Persuasion" , "Speaking" ; + rdfs:comment "Influencing others in favor of a product, service, or point of view."@en . + +:peopleSpeakingSignalling a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Speaking-Signalling"@en ; + :fjaDomain "people" ; :fjaRank 6 ; + :cognitiveFacet "Oral Comprehension" ; + :behavioralFacet "Speaking" ; + rdfs:comment "Talking with and/or signalling people to convey or exchange information. Includes giving assignments and/or directions to helpers or assistants."@en . + +:peopleServing a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Serving"@en ; + :fjaDomain "people" ; :fjaRank 7 ; + :affectiveFacet "Service Orientation" ; + :behavioralFacet "Active Listening" ; + rdfs:comment "Attending to the needs or requests of people or animals or the expressed or implicit wishes of people. Immediate response is involved."@en . + +:peopleTakingInstructionsHelping a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Taking Instructions-Helping"@en ; + :fjaDomain "people" ; :fjaRank 8 ; + :affectiveFacet "Cooperation" ; + :behavioralFacet "Active Listening" ; + rdfs:comment "Attending to the work assignment instructions or orders of supervisor. (No immediate response required unless clarification of instructions or orders is needed.)"@en . + +# ---- Things (6th DOT digit): inanimate objects as defined by DOT ---- + +:thingsSettingUp a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Setting Up"@en ; + :fjaDomain "things" ; :fjaRank 0 ; + :behavioralFacet "Manual Dexterity" , "Equipment Maintenance" ; + rdfs:comment "Preparing machines, equipment, or work stations for use by adjusting fixtures, controls, or settings according to job requirements."@en . + +:thingsPrecisionWorking a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Precision Working"@en ; + :fjaDomain "things" ; :fjaRank 1 ; + :behavioralFacet "Control Precision" , "Arm-Hand Steadiness" ; + rdfs:comment "Using body members and/or tools or work aids to work on, move, guide, or place objects or materials in situations where ultimate responsibility for the attainment of standards occurs and where the selection of appropriate tools, objects, or materials and the adjustment of the tool to the task require considerable tolerance for judgment."@en . + +:thingsOperatingControlling a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Operating-Controlling"@en ; + :fjaDomain "things" ; :fjaRank 2 ; + :cognitiveFacet "Operation Monitoring" ; + :behavioralFacet "Rate Control" , "Reaction Time" ; + rdfs:comment "Starting, stopping, and controlling the actions of machines and equipment by turning valves, switches, dials, buttons, levers, knobs, and similar devices, either singly or in combinations."@en . + +:thingsDrivingOperating a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Driving-Operating"@en ; + :fjaDomain "things" ; :fjaRank 3 ; + :cognitiveFacet "Spatial Orientation" ; + :behavioralFacet "Reaction Time" , "Response Orientation" ; + rdfs:comment "Starting, stopping, and controlling the actions of machines or vehicles, such as automobiles, trucks, tractors, buses, cranes, hoists, conveyors, and like equipment, that are operated primarily by steering or by manipulation of controls."@en . + +:thingsManipulating a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Manipulating"@en ; + :fjaDomain "things" ; :fjaRank 4 ; + :behavioralFacet "Multilimb Coordination" , "Manual Dexterity" ; + rdfs:comment "Using body members, handtools, and/or special devices to work on, move, guide, or place objects or materials."@en . + +:thingsTending a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Tending"@en ; + :fjaDomain "things" ; :fjaRank 5 ; + :cognitiveFacet "Vigilance" ; + :behavioralFacet "Reaction Time" ; + rdfs:comment "Starting, stopping, and observing the functioning of machines and equipment by manipulating controls according to a relatively fixed routine and/or by making adjustments when signaled or informed that the operation requires correction."@en . + +:thingsFeedingOffbearing a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Feeding-Offbearing"@en ; + :fjaDomain "things" ; :fjaRank 6 ; + :behavioralFacet "Extent Flexibility" , "Static Strength" ; + rdfs:comment "Inserting, throwing, dumping, or placing materials into or removing them from machines or equipment which are automatic or tended or operated by other workers."@en . + +:thingsHandling a skos:Concept , :WorkerFunction ; + skos:inScheme :workerFunctionScheme ; + skos:prefLabel "Handling"@en ; + :fjaDomain "things" ; :fjaRank 7 ; + :behavioralFacet "Manual Dexterity" , "Finger Dexterity" ; + rdfs:comment "Using body members, handtools, and/or special devices to install, remove, or otherwise handle materials or objects."@en . diff --git a/docs/product-requirements.md b/docs/product-requirements.md index 0d456f2ae..f1f2fd3f7 100644 --- a/docs/product-requirements.md +++ b/docs/product-requirements.md @@ -56,6 +56,23 @@ edge exposes the same authorized endpoints and evidence through API and UI. Acceptance: Turtle, JSON-LD, N-Triples, SHACL, API payloads, persisted IRIs, and rendered labels agree on term kind, direction, namespace, and provenance. +### PRD-FR-2A — Worker-function taxonomy + +- Publish the DOT/FJA Data/People/Things worker functions (24 concepts, + official definitions verbatim) in the canonical ontology namespace + (ADR 0232), each with its definitional ordinal rank and qualitative + cognitive/affective/behavioral facet tags drawn only from published + Fleishman ability and O*NET skill/work-style vocabularies. +- Expose the taxonomy through a deterministic application read model with + fail-closed lookups; an absent function is an honest unknown. +- Carry no numeric weight from the taxonomy: ranks are scale positions, + never calibrated weights. + +Acceptance: completeness, verbatim definition, closed facet vocabulary, +deterministic ordering, and lookup round-trip isolation are enforced by +`tests/test_worker_function_taxonomy.py`; `tests/test_ontology.py` +continues to pass unchanged. + ### PRD-FR-3 — Bounded ontology exploration - Apply RBAC/ABAC, source eligibility, and knowledge cutoff before graph diff --git a/lineageweave/worker_function_taxonomy.py b/lineageweave/worker_function_taxonomy.py new file mode 100644 index 000000000..4bab0fbce --- /dev/null +++ b/lineageweave/worker_function_taxonomy.py @@ -0,0 +1,222 @@ +"""The DOT/FJA worker-function taxonomy as a typed read model over the +published ontology (ADR 0232). + +Functional Job Analysis expresses every job's relationship to *Data*, +*People*, and *Things* through three ordered worker-function lists that +the Dictionary of Occupational Titles carried verbatim in Appendix B +(U.S. Department of Labor, 1991): Data ranks 0-6, People ranks 0-8, +Things ranks 0-7, each ordered so the lower digit names the more complex +function. This module is the application-side projection of those +concepts from `docs/ontology/lineageweave-kg.ttl`, where they live as a +`skos:ConceptScheme` of `:WorkerFunction` concepts carrying the official +definitions, their definitional ordinal ranks, and qualitative +cognitive / affective / behavioral facet tags. + +Provenance discipline mirrors the rest of this repository: + +- Ranks are scale positions copied from the published table -- never + fitted, calibrated, or renormalized here. Nothing in this module may + produce numeric weights (measurement stays governed by ADR 0145). +- Facet tags name published Fleishman ability families and O*NET basic / + cross-functional skills (Fleishman, Costanza, & Marshall-Mies, 1999; + Mumford, Peterson, & Childs, 1999) projected editorially onto each + official definition. They carry no weight and make no calibration + claim. +- Lookups fail closed: an absent concept returns ``None``/empty rather + than a placeholder, the same missing-vs-negative rule as the Null + channels. + +References +---------- +Fine, S. A., & Cronshaw, S. F. (1999). *Functional job analysis: A +foundation for human resources management*. Lawrence Erlbaum +Associates. + +U.S. Department of Labor. (1991). *Dictionary of occupational titles* +(4th ed., rev., Appendix B). U.S. Government Printing Office. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from functools import lru_cache + +from rdflib import URIRef +from rdflib.namespace import RDFS, SKOS + +from .ontology import LW, ONTOLOGY + +#: The three DOT lists with their published rank ranges. The bounds are +#: definitional table extents (U.S. Department of Labor, 1991, Appendix +#: B), not tunable parameters. +WORKER_FUNCTION_DOMAINS: dict[str, tuple[int, int]] = { + "data": (0, 6), + "people": (0, 8), + "things": (0, 7), +} + +#: Canonical DOT digit order -- Data is the 4th code digit, People the +#: 5th, Things the 6th -- used for deterministic sorting. +_DOMAIN_ORDER: tuple[str, ...] = ("data", "people", "things") + +#: Facet annotation groups read off each concept, mapped to the record +#: field names they populate. +_FACET_PROPERTIES: tuple[tuple[str, URIRef], ...] = ( + ("cognitive_facets", LW.cognitiveFacet), + ("affective_facets", LW.affectiveFacet), + ("behavioral_facets", LW.behavioralFacet), +) + + +@dataclass(frozen=True) +class WorkerFunctionRecord: + """One worker-function concept exactly as the ontology declares it. + + Attributes mirror the TTL annotations one-for-one; nothing is + derived, inferred, or scored at read time. + """ + + iri: str + """The canonical repository-case ontology IRI for this function.""" + + domain: str + """Which DOT list the function belongs to: ``data``, ``people``, or + ``things``.""" + + rank: int + """The function's definitional position on its list; the lower digit + names the more complex function.""" + + label: str + """The SKOS preferred label, e.g. ``"Synthesizing"``.""" + + definition: str + """The official DOT Appendix B definition, stored verbatim as the + term's ``rdfs:comment``.""" + + cognitive_facets: tuple[str, ...] + """Published Fleishman ability families or O*NET skills exercised + cognitively, alphabetically sorted for deterministic output.""" + + affective_facets: tuple[str, ...] + """Published O*NET work styles or social skills exercised + affectively, alphabetically sorted for deterministic output.""" + + behavioral_facets: tuple[str, ...] + """Published Fleishman psychomotor/physical abilities or O*NET + behavioral skills exercised behaviorally, alphabetically sorted for + deterministic output.""" + + +def _facet_values(subject: URIRef) -> dict[str, tuple[str, ...]]: + """Read the three facet annotation groups off one subject. + + Values are collected into sets first so duplicate annotations can + never surface twice, then returned as deterministically sorted + tuples keyed by record field name. + """ + values: dict[str, tuple[str, ...]] = {} + for field_name, predicate in _FACET_PROPERTIES: + seen = {str(value) for value in ONTOLOGY.objects(subject, predicate)} + values[field_name] = tuple(sorted(seen)) + return values + + +def _record_for(subject: URIRef) -> WorkerFunctionRecord: + """Build one record from its ontology subject. + + Raises ``ValueError`` when a declared concept is missing one of its + structural annotations (domain, rank, label, or definition): a + malformed declaration must surface loudly rather than degrade into + an invented default, matching the repository's fail-closed rule. + """ + domain_literal = ONTOLOGY.value(subject, LW.fjaDomain) + rank_literal = ONTOLOGY.value(subject, LW.fjaRank) + label_literal = ONTOLOGY.value(subject, SKOS.prefLabel) + definition_literal = ONTOLOGY.value(subject, RDFS.comment) + missing = [ + name + for name, literal in ( + (":fjaDomain", domain_literal), + (":fjaRank", rank_literal), + ("skos:prefLabel", label_literal), + ("rdfs:comment", definition_literal), + ) + if literal is None + ] + if missing: + raise ValueError( + f"worker-function term {subject} is missing required " + f"annotations: {', '.join(missing)}" + ) + domain = str(domain_literal) + if domain not in WORKER_FUNCTION_DOMAINS: + raise ValueError( + f"worker-function term {subject} declares unknown :fjaDomain " + f"{domain!r}" + ) + facets = _facet_values(subject) + return WorkerFunctionRecord( + iri=str(subject), + domain=domain, + rank=int(rank_literal), + label=str(label_literal), + definition=str(definition_literal), + cognitive_facets=facets["cognitive_facets"], + affective_facets=facets["affective_facets"], + behavioral_facets=facets["behavioral_facets"], + ) + + +@lru_cache(maxsize=1) +def worker_function_records() -> tuple[WorkerFunctionRecord, ...]: + """Every declared worker-function concept, deterministically sorted. + + Sorting follows the DOT code-digit order (Data, People, Things) and, + within a domain, ascending rank. Deterministic output keeps + downstream serialization byte-stable across processes, matching the + repository's deterministic-artifact rules. + """ + records = [ + _record_for(subject) + for subject in ONTOLOGY.subjects(SKOS.inScheme, LW.workerFunctionScheme) + ] + domain_index = {name: index for index, name in enumerate(_DOMAIN_ORDER)} + records.sort(key=lambda record: (domain_index[record.domain], record.rank)) + return tuple(records) + + +def worker_function(domain: str, rank: int) -> WorkerFunctionRecord | None: + """One worker function by its DOT domain and rank, or ``None``. + + ``None`` means the pair is genuinely undeclared -- the honest + unknown -- never a placeholder. An unrecognized ``domain`` raises + ``ValueError`` because it is caller error, not missing evidence. + """ + if domain not in WORKER_FUNCTION_DOMAINS: + raise ValueError( + f"unknown worker-function domain {domain!r}; expected one of " + f"{sorted(WORKER_FUNCTION_DOMAINS)}" + ) + for record in worker_function_records(): + if record.domain == domain and record.rank == rank: + return record + return None + + +def facets_for(domain: str, rank: int) -> dict[str, tuple[str, ...]]: + """The facet tag groups for one worker function. + + Returns the three sorted facet tuples for a declared function and + ``{}`` for an undeclared rank inside a valid domain -- the same + missing-vs-negative discipline as `ontology_annotations`. An + unrecognized ``domain`` raises ``ValueError``. + """ + record = worker_function(domain, rank) + if record is None: + return {} + return { + "cognitive_facets": record.cognitive_facets, + "affective_facets": record.affective_facets, + "behavioral_facets": record.behavioral_facets, + } diff --git a/pyproject.toml b/pyproject.toml index e98205768..8a1288a69 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "lineageweave" -version = "2.18.0" +version = "2.19.0" description = "Reconstructs git-branch-style lineage DAGs from scattered short records using multi-channel score fusion and LLM adjudication." readme = "README.md" license = { text = "MIT" } diff --git a/tests/test_worker_function_taxonomy.py b/tests/test_worker_function_taxonomy.py new file mode 100644 index 000000000..29e2a9466 --- /dev/null +++ b/tests/test_worker_function_taxonomy.py @@ -0,0 +1,247 @@ +"""Correctness checks for the DOT/FJA worker-function read model +(ADR 0232). + +The tests treat the published Dictionary of Occupational Titles +Appendix B tables as ground truth: every declared concept must carry +the official definition verbatim, the definitional rank from the +published table, and facet tags drawn only from the published Fleishman +ability / O*NET skill vocabularies. Nothing here may accept an invented +weight or a placeholder for missing evidence. +""" + +from __future__ import annotations + +import pytest +from rdflib import RDF + +from lineageweave.ontology import LW, ONTOLOGY, all_declared_lookup_codes +from lineageweave.worker_function_taxonomy import ( + WORKER_FUNCTION_DOMAINS, + facets_for, + worker_function, + worker_function_records, +) + +#: Verbatim opening fragments of official DOT Appendix B definitions, +#: keyed by ``(domain, rank)`` -- a real-world accuracy check that the +#: ontology carries the published text rather than a paraphrase. +_OFFICIAL_DEFINITION_PREFIXES: dict[tuple[str, int], str] = { + ("data", 0): "Integrating analyses of data to discover facts", + ("data", 1): "Determining time, place, and sequence of operations", + ("data", 2): "Examining and evaluating data.", + ("data", 3): "Gathering, collating, or classifying information about data", + ("data", 4): "Performing arithmetic operations", + ("data", 5): "Transcribing, entering, or posting data.", + ("data", 6): "Judging the readily observable functional, structural", + ("people", 0): "Dealing with individuals in terms of their total personality", + ("people", 1): "Exchanging ideas, information, and opinions with others", + ("people", 2): "Teaching subject matter to others", + ("people", 3): "Determining or interpreting work procedures", + ("people", 4): "Amusing others", + ("people", 5): "Influencing others in favor of a product, service", + ("people", 6): "Talking with and/or signalling people", + ("people", 7): "Attending to the needs or requests of people", + ("people", 8): "Attending to the work assignment instructions or orders", + ("things", 0): "Preparing machines, equipment, or work stations", + ("things", 1): "Using body members and/or tools or work aids to work on", + ("things", 2): "Starting, stopping, and controlling the actions of machines and equipment", + ("things", 3): "Starting, stopping, and controlling the actions of machines or vehicles", + ("things", 4): "Using body members, handtools, and/or special devices", + ("things", 5): "Starting, stopping, and observing the functioning of machines", + ("things", 6): "Inserting, throwing, dumping, or placing materials", + ("things", 7): "Using body members, handtools, and/or special devices to install", +} + +#: The closed facet-tag vocabulary this ADR allows: published Fleishman +#: ability families plus O*NET basic / cross-functional skill names. +_ALLOWED_FACET_TAGS = { + # Fleishman ability families (Fleishman et al., 1999) + "Arm-Hand Steadiness", + "Category Flexibility", + "Control Precision", + "Deductive Reasoning", + "Extent Flexibility", + "Finger Dexterity", + "Fluency of Ideas", + "Inductive Reasoning", + "Manual Dexterity", + "Mathematical Reasoning", + "Multilimb Coordination", + "Number Facility", + "Oral Comprehension", + "Oral Expression", + "Perceptual Speed", + "Rate Control", + "Reaction Time", + "Response Orientation", + "Spatial Orientation", + "Static Strength", + "Vigilance", + "Written Comprehension", + # O*NET basic / cross-functional skills and work styles + # (Mumford et al., 1999; Peterson et al., 1999) + "Active Listening", + "Cooperation", + "Coordination", + "Critical Thinking", + "Equipment Maintenance", + "Instructing", + "Management of Personnel Resources", + "Monitoring", + "Negotiation", + "Operation Monitoring", + "Persuasion", + "Service Orientation", + "Social Perceptiveness", + "Speaking", + "Time Management", +} + + +def _record_map() -> dict[tuple[str, int], object]: + """Index every declared record by its ``(domain, rank)`` pair.""" + return {(record.domain, record.rank): record for record in worker_function_records()} + + +def test_every_published_worker_function_is_declared() -> None: + """The taxonomy declares exactly the 24 DOT functions: Data 0-6, + People 0-8, Things 0-7 -- no more, no fewer.""" + assert len(WORKER_FUNCTION_DOMAINS) == 3 + assert len(worker_function_records()) == 24 + + +def test_domain_ranks_match_the_published_table_extents() -> None: + """Each domain declares exactly one concept per published rank.""" + records_by_domain: dict[str, set[int]] = {} + for record in worker_function_records(): + records_by_domain.setdefault(record.domain, set()).add(record.rank) + for domain, (low, high) in WORKER_FUNCTION_DOMAINS.items(): + assert records_by_domain[domain] == set(range(low, high + 1)) + + +def test_definitions_carry_the_official_dot_text() -> None: + """Every concept's comment is the official Appendix B definition, + verified against verbatim opening fragments.""" + indexed = _record_map() + for key, prefix in _OFFICIAL_DEFINITION_PREFIXES.items(): + record = indexed[key] + assert record.definition.startswith(prefix), ( + f"{key} definition drifted from the official text: " + f"{record.definition!r}" + ) + + +def test_labels_are_unique_across_the_scheme() -> None: + """No two functions share a preferred label, so label-based display + can never conflate two ranks.""" + labels = [record.label for record in worker_function_records()] + assert len(labels) == len(set(labels)) + + +def test_facet_tags_stay_inside_the_published_vocabularies() -> None: + """Every facet tag names a Fleishman ability family or O*NET skill + from the allowed set -- no invented construct may enter the layer.""" + for record in worker_function_records(): + all_tags = ( + *record.cognitive_facets, + *record.affective_facets, + *record.behavioral_facets, + ) + unknown = set(all_tags) - _ALLOWED_FACET_TAGS + assert not unknown, f"{(record.domain, record.rank)} invented facets: {unknown}" + + +def test_every_function_exercises_at_least_one_facet() -> None: + """Each definition projects onto at least one facet group; a fully + untagged function would mean the projection silently failed.""" + for record in worker_function_records(): + total = ( + len(record.cognitive_facets) + + len(record.affective_facets) + + len(record.behavioral_facets) + ) + assert total >= 1, f"{(record.domain, record.rank)} has no facets" + + +def test_records_are_sorted_in_dot_digit_order_then_rank() -> None: + """Output order is Data, People, Things with ascending ranks inside + each domain -- byte-stable serialization input.""" + records = worker_function_records() + domain_order = ["data"] * 7 + ["people"] * 9 + ["things"] * 8 + assert [record.domain for record in records] == domain_order + assert [record.rank for record in records[:7]] == list(range(0, 7)) + assert [record.rank for record in records[7:16]] == list(range(0, 9)) + assert [record.rank for record in records[16:]] == list(range(0, 8)) + + +def test_iri_is_the_canonical_repository_case_namespace() -> None: + """Every record IRI uses the repository-case canonical namespace; + no lowercase compatibility IRI may be minted here (ADR 0207).""" + canonical_prefix = "https://contextualwisdomlab.github.io/LineageWeave/ontology#" + for record in worker_function_records(): + assert record.iri.startswith(canonical_prefix) + + +def test_worker_functions_carry_no_lookup_code() -> None: + """Worker-function concepts deliberately declare no ``lookupCode``, + so the lookup-code round trip stays untouched by this extension.""" + from lineageweave.ontology import iri_for_lookup_code + + assert iri_for_lookup_code("worker_function_data_synthesizing") is None + # The pre-existing governed vocabulary is unchanged in size: the 31 + # lookup codes the schema seeded before this ADR are still all there + # is. + assert len(all_declared_lookup_codes()) == 31 + + +@pytest.mark.parametrize( + ("domain", "rank", "expected_label"), + [ + ("data", 0, "Synthesizing"), + ("people", 0, "Mentoring"), + ("people", 8, "Taking Instructions-Helping"), + ("things", 0, "Setting Up"), + ("things", 7, "Handling"), + ], +) +def test_worker_function_resolves_real_pairs(domain: str, rank: int, expected_label: str) -> None: + """Published (domain, rank) pairs resolve to their labeled record.""" + record = worker_function(domain, rank) + assert record is not None + assert record.label == expected_label + + +def test_worker_function_returns_none_for_an_absent_rank() -> None: + """An undeclared rank inside a valid domain is an honest unknown, + never a placeholder record.""" + assert worker_function("data", 99) is None + + +def test_worker_function_raises_on_an_unknown_domain() -> None: + """An unrecognized domain is caller error, not missing evidence.""" + with pytest.raises(ValueError, match="unknown worker-function domain"): + worker_function("machines", 0) + + +def test_facets_for_returns_groups_for_a_declared_pair() -> None: + """Facet lookup returns exactly the record's three sorted groups.""" + record = worker_function("data", 2) + assert record is not None + facets = facets_for("data", 2) + assert facets == { + "cognitive_facets": record.cognitive_facets, + "affective_facets": record.affective_facets, + "behavioral_facets": record.behavioral_facets, + } + assert facets["cognitive_facets"] == tuple(sorted(facets["cognitive_facets"])) + + +def test_facets_for_is_empty_for_an_undeclared_rank() -> None: + """Missing-vs-negative discipline mirrors ontology_annotations().""" + assert facets_for("things", 42) == {} + + +def test_facets_for_raises_on_an_unknown_domain() -> None: + """Domain validation propagates through the facet helper too.""" + with pytest.raises(ValueError, match="unknown worker-function domain"): + facets_for("vibes", 0) diff --git a/uv.lock b/uv.lock index a29ef2708..59b2a5673 100644 --- a/uv.lock +++ b/uv.lock @@ -685,7 +685,7 @@ wheels = [ [[package]] name = "lineageweave" -version = "2.18.0" +version = "2.19.0" source = { editable = "." } dependencies = [ { name = "certifi" }, From 2fc2f77002025a35016a1845c2876cbfa2cae798 Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 26 Aug 2026 21:32:18 +0900 Subject: [PATCH 02/12] docs(ontology): clarify behavioral facet projection --- docs/ontology/lineageweave-kg.ttl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ontology/lineageweave-kg.ttl b/docs/ontology/lineageweave-kg.ttl index 0daab5e43..f1365b936 100644 --- a/docs/ontology/lineageweave-kg.ttl +++ b/docs/ontology/lineageweave-kg.ttl @@ -490,7 +490,7 @@ :behavioralFacet a owl:AnnotationProperty ; rdfs:label "behavioral facet"@en ; - rdfs:comment "A named Fleishman psychomotor/physical ability or O*NET behavioral skill that the official function definition exercises behaviorally. Editorial projection only; no weight or calibration."@en . + rdfs:comment "A named Fleishman ability or published O*NET basic/cross-functional skill that the official function definition exercises as an observable behavior. This editorial projection is not an O*NET skill-category assertion and carries no weight or calibration."@en . # ---- Data (4th DOT digit): information, knowledge, and conceptions ---- From 91f342f4edf2f4d84d95379dcf3aaa38d4a79c16 Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 26 Aug 2026 21:48:25 +0900 Subject: [PATCH 03/12] fix(ontology): remove unsupported facet crosswalk --- CHANGELOG.md | 6 +- ...tion-taxonomy-in-the-published-ontology.md | 52 +++------ .../WORKER_FUNCTION_TAXONOMY_REFERENCES.md | 27 +---- docs/ontology/lineageweave-kg.ttl | 54 +-------- docs/product-requirements.md | 9 +- lineageweave/worker_function_taxonomy.py | 67 +----------- tests/test_worker_function_taxonomy.py | 103 +----------------- 7 files changed, 29 insertions(+), 289 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f09e7d40..f422ed9ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,10 +10,8 @@ All notable changes to this project are documented here. Format follows - The DOT/FJA Data/People/Things worker-function taxonomy is now published in the canonical ontology: all 24 worker functions carry the official - Dictionary of Occupational Titles Appendix B definitions verbatim, their - definitional ordinal ranks, and qualitative cognitive, affective, and - behavioral facet tags drawn only from published Fleishman ability and - O*NET skill vocabularies (ADR 0232). A deterministic application read + Dictionary of Occupational Titles Appendix B definitions verbatim and + their definitional ordinal ranks (ADR 0232). A deterministic application read model (`lineageweave.worker_function_taxonomy`) exposes fail-closed lookups; ranks are scale positions and are never used as weights. - Persist explicit paragraph, list, table, MathML formula, and caller-parsed diff --git a/docs/adr/0232-worker-function-taxonomy-in-the-published-ontology.md b/docs/adr/0232-worker-function-taxonomy-in-the-published-ontology.md index bc6e3529d..ee2729421 100644 --- a/docs/adr/0232-worker-function-taxonomy-in-the-published-ontology.md +++ b/docs/adr/0232-worker-function-taxonomy-in-the-published-ontology.md @@ -13,13 +13,6 @@ through three ordered lists, carried verbatim in the Dictionary of Occupational Titles Appendix B (U.S. Department of Labor, 1991): Data ranks 0-6, People ranks 0-8, Things ranks 0-7, each ordered so that the lower digit names the more complex function (Fine & Cronshaw, 1999). -Each function definition exercises identifiable cognitive, -affective/social, and behavioral facets that map onto the published -Fleishman ability taxonomy and O*NET basic and cross-functional skills -(Fleishman & Quaintance, 1984; Fleishman, Costanza, & -Marshall-Mies, 1999; Mumford, Peterson, & Childs, 1999; Peterson et al., -1999). - The repository had no representation for this vocabulary: a post or analysis cannot yet say "this evidence describes Analyzing-level data work" without inventing an untracked term. The ontology is the correct @@ -38,10 +31,7 @@ PostgreSQL graph facts -- but two constraints bind any addition: (`:workerFunctionScheme`) of `:WorkerFunction` concepts in `docs/ontology/lineageweave-kg.ttl`, each carrying the official DOT Appendix B definition verbatim as its `rdfs:comment`, its definitional - rank (`:fjaRank`), its domain (`:fjaDomain`), and qualitative facet - tags (`:cognitiveFacet` / `:affectiveFacet` / `:behavioralFacet`) - naming only published Fleishman ability families or O*NET skill and - work-style constructs. + rank (`:fjaRank`) and its domain (`:fjaDomain`). 2. Like column-projection datatype properties, these concepts carry no `:lookupCode`: they are not `common_lookup_value` rows, so the round trip is untouched. Binding a function to stored rows needs a separate @@ -57,28 +47,32 @@ PostgreSQL graph facts -- but two constraints bind any addition: malformed TTL declaration. 5. The canonical repository-case namespace (ADR 0207) mints every IRI; no lowercase compatibility form is introduced. +6. Do not publish cognitive, affective, or behavioral crosswalks from + the DOT/FJA functions to Fleishman or O*NET constructs unless an + authoritative source publishes the exact term-level mapping. The + initial branch's `:cognitiveFacet`, `:affectiveFacet`, and + `:behavioralFacet` projection was editorial rather than sourced, so + it is removed before delivery under the repository's no-heuristic + and provenance-preservation contracts. ## Consequences - The IO-psychology worker-function vocabulary becomes addressable and citable inside the published semantic layer before any persistence decision exists. -- Facet tags are an editorial projection of official definitions onto - published taxonomies. They carry no weight and make no calibration - claim; presenting them as measured requirements would violate the - measurement boundary. +- The ontology does not imply an unsupported ability, skill, work-style, + or behavioral requirement from the official function definitions. - Adding DB-backed function annotations later means one migration plus seed rows and `:lookupCode` declarations -- the extension path is additive by construction. - `tests/test_worker_function_taxonomy.py` pins the published text with - verbatim definition prefixes and closes the facet vocabulary, so - drift toward invented constructs fails CI. + verbatim definition prefixes, so definition drift fails CI. ## Verification - `tests/test_worker_function_taxonomy.py`: completeness (24 concepts), - per-domain rank extents, verbatim official-definition prefixes, closed - facet vocabulary, deterministic ordering, canonical namespace, + per-domain rank extents, verbatim official-definition prefixes, + deterministic ordering, canonical namespace, fail-closed lookups. - `tests/test_ontology.py` continues to pass unchanged: the round trip sees no new lookup codes. @@ -89,26 +83,6 @@ Fine, S. A., & Cronshaw, S. F. (1999). *Functional job analysis: A foundation for human resources management*. Lawrence Erlbaum Associates. -Fleishman, E. A., & Quaintance, M. K. (1984). *Taxonomies of human -performance: The description of human tasks*. Academic Press. - -Fleishman, E. A., Costanza, D. P., & Marshall-Mies, J. C. (1999). -Abilities. In N. G. Peterson, M. D. Mumford, W. C. Borman, P. R. -Jeanneret, & E. A. Fleishman (Eds.), *An occupational information system -for the 21st century: The development of O*NET* (pp. 97-112). American -Psychological Association. - -Mumford, M. D., Peterson, N. G., & Childs, R. A. (1999). Basic and -cross-functional skills. In N. G. Peterson, M. D. Mumford, W. C. Borman, -P. R. Jeanneret, & E. A. Fleishman (Eds.), *An occupational information -system for the 21st century: The development of O*NET* (pp. 49-69). -American Psychological Association. - -Peterson, N. G., Mumford, M. D., Borman, W. C., Jeanneret, P. R., & -Fleishman, E. A. (Eds.). (1999). *An occupational information system for -the 21st century: The development of O\*NET*. American Psychological -Association. - U.S. Department of Labor. (1991). *Dictionary of occupational titles* (4th ed., rev., Appendix B). U.S. Government Printing Office. https://www.dol.gov/agencies/oalj/PUBLIC/DOT/REFERENCES/DOTAPPB diff --git a/docs/doctoring/WORKER_FUNCTION_TAXONOMY_REFERENCES.md b/docs/doctoring/WORKER_FUNCTION_TAXONOMY_REFERENCES.md index 9482ecb92..8abf59e11 100644 --- a/docs/doctoring/WORKER_FUNCTION_TAXONOMY_REFERENCES.md +++ b/docs/doctoring/WORKER_FUNCTION_TAXONOMY_REFERENCES.md @@ -3,32 +3,12 @@ Supporting literature for [ADR 0232](../adr/0232-worker-function-taxonomy-in-the-published-ontology.md). APA 7th edition. This list is the citation record for the DOT/FJA -Data/People/Things worker functions and their cognitive, affective, and -behavioral facet projections. +Data/People/Things worker functions and their official definitions and +rank positions. Fine, S. A., & Cronshaw, S. F. (1999). *Functional job analysis: A foundation for human resources management*. Lawrence Erlbaum Associates. -Fleishman, E. A., & Quaintance, M. K. (1984). *Taxonomies of human -performance: The description of human tasks*. Academic Press. - -Fleishman, E. A., Costanza, D. P., & Marshall-Mies, J. C. (1999). -Abilities. In N. G. Peterson, M. D. Mumford, W. C. Borman, P. R. -Jeanneret, & E. A. Fleishman (Eds.), *An occupational information system -for the 21st century: The development of O\*NET* (pp. 97–112). American -Psychological Association. - -Mumford, M. D., Peterson, N. G., & Childs, R. A. (1999). Basic and -cross-functional skills. In N. G. Peterson, M. D. Mumford, W. C. Borman, -P. R. Jeanneret, & E. A. Fleishman (Eds.), *An occupational information -system for the 21st century: The development of O\*NET* (pp. 49–69). -American Psychological Association. - -Peterson, N. G., Mumford, M. D., Borman, W. C., Jeanneret, P. R., & -Fleishman, E. A. (Eds.). (1999). *An occupational information system for -the 21st century: The development of O\*NET*. American Psychological -Association. - U.S. Department of Labor. (1991). *Dictionary of occupational titles* (4th ed., rev., Appendix B). U.S. Government Printing Office. https://www.dol.gov/agencies/oalj/PUBLIC/DOT/REFERENCES/DOTAPPB @@ -47,3 +27,6 @@ DOT table; they are definitional scale positions and never fitted or calibrated weights. Score interpretation and fairness remain governed by AERA, APA, and NCME (2014), cited in the repository's measurement-boundary ADRs ([ADR 0145](../adr/0145-psychometric-channel-weight-estimation.md)). +No authoritative source in this register publishes an exact term-level +crosswalk from the 24 DOT/FJA functions to Fleishman or O*NET constructs; +therefore this ontology does not publish one (ADR 0232). diff --git a/docs/ontology/lineageweave-kg.ttl b/docs/ontology/lineageweave-kg.ttl index f1365b936..6fede4ab6 100644 --- a/docs/ontology/lineageweave-kg.ttl +++ b/docs/ontology/lineageweave-kg.ttl @@ -446,13 +446,7 @@ # carries the official DOT definition verbatim as its rdfs:comment, # its definitional ordinal rank (:fjaRank -- lower digits denote the # more complex function; these are scale positions, never fitted or -# calibrated weights), and qualitative facet tags naming published -# Fleishman ability families and O*NET basic / cross-functional skills -# (Fleishman et al., 1999; Mumford et al., 1999; Peterson et al., -# 1999). Facet tags are an editorial projection of each official -# definition onto those taxonomies: they carry no numeric weight, no -# calibration, and no measurement claim -- channel-weight estimation -# stays governed by ADR 0145. +# calibrated weights). Channel-weight estimation stays governed by ADR 0145. # # Like column-projection properties above, these concepts deliberately # do NOT carry :lookupCode: they are not common_lookup_value rows, so @@ -480,69 +474,48 @@ rdfs:label "FJA rank"@en ; rdfs:comment "The function's definitional position on its DOT list. Lower digits name the more complex function; the digit is a scale position from the published table, not a fitted weight."@en . -:cognitiveFacet a owl:AnnotationProperty ; - rdfs:label "cognitive facet"@en ; - rdfs:comment "A named Fleishman ability family or O*NET skill that the official function definition exercises cognitively. Editorial projection only; no weight or calibration."@en . - -:affectiveFacet a owl:AnnotationProperty ; - rdfs:label "affective facet"@en ; - rdfs:comment "A named O*NET work style or social skill that the official function definition exercises affectively. Editorial projection only; no weight or calibration."@en . - -:behavioralFacet a owl:AnnotationProperty ; - rdfs:label "behavioral facet"@en ; - rdfs:comment "A named Fleishman ability or published O*NET basic/cross-functional skill that the official function definition exercises as an observable behavior. This editorial projection is not an O*NET skill-category assertion and carries no weight or calibration."@en . - # ---- Data (4th DOT digit): information, knowledge, and conceptions ---- :dataSynthesizing a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Synthesizing"@en ; :fjaDomain "data" ; :fjaRank 0 ; - :cognitiveFacet "Inductive Reasoning" , "Fluency of Ideas" ; rdfs:comment "Integrating analyses of data to discover facts and/or develop knowledge concepts or interpretations."@en . :dataCoordinating a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Coordinating"@en ; :fjaDomain "data" ; :fjaRank 1 ; - :cognitiveFacet "Time Management" , "Coordination" ; - :behavioralFacet "Oral Expression" ; rdfs:comment "Determining time, place, and sequence of operations or action to be taken on the basis of analysis of data; executing determinations and/or reporting on events."@en . :dataAnalyzing a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Analyzing"@en ; :fjaDomain "data" ; :fjaRank 2 ; - :cognitiveFacet "Deductive Reasoning" , "Inductive Reasoning" , "Critical Thinking" ; rdfs:comment "Examining and evaluating data. Presenting alternative actions in relation to the evaluation is frequently involved."@en . :dataCompiling a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Compiling"@en ; :fjaDomain "data" ; :fjaRank 3 ; - :cognitiveFacet "Category Flexibility" , "Written Comprehension" ; rdfs:comment "Gathering, collating, or classifying information about data, people, or things. Reporting and/or carrying out a prescribed action in relation to the information is frequently involved."@en . :dataComputing a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Computing"@en ; :fjaDomain "data" ; :fjaRank 4 ; - :cognitiveFacet "Number Facility" , "Mathematical Reasoning" ; rdfs:comment "Performing arithmetic operations and reporting on and/or carrying out a prescribed action in relation to them. Does not include counting."@en . :dataCopying a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Copying"@en ; :fjaDomain "data" ; :fjaRank 5 ; - :cognitiveFacet "Written Comprehension" ; - :behavioralFacet "Finger Dexterity" ; rdfs:comment "Transcribing, entering, or posting data."@en . :dataComparing a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Comparing"@en ; :fjaDomain "data" ; :fjaRank 6 ; - :cognitiveFacet "Perceptual Speed" ; rdfs:comment "Judging the readily observable functional, structural, or compositional characteristics (whether similar to or divergent from obvious standards) of data, people, or things."@en . # ---- People (5th DOT digit): human beings dealt with individually ---- @@ -551,68 +524,54 @@ skos:inScheme :workerFunctionScheme ; skos:prefLabel "Mentoring"@en ; :fjaDomain "people" ; :fjaRank 0 ; - :affectiveFacet "Social Perceptiveness" ; - :behavioralFacet "Instructing" , "Active Listening" ; rdfs:comment "Dealing with individuals in terms of their total personality in order to advise, counsel, and/or guide them with regard to problems that may be resolved by legal, scientific, clinical, spiritual, and/or other professional principles."@en . :peopleNegotiating a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Negotiating"@en ; :fjaDomain "people" ; :fjaRank 1 ; - :affectiveFacet "Social Perceptiveness" ; - :behavioralFacet "Negotiation" , "Persuasion" ; rdfs:comment "Exchanging ideas, information, and opinions with others to formulate policies and programs and/or arrive jointly at decisions, conclusions, or solutions."@en . :peopleInstructing a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Instructing"@en ; :fjaDomain "people" ; :fjaRank 2 ; - :behavioralFacet "Instructing" , "Speaking" ; rdfs:comment "Teaching subject matter to others, or training others (including animals) through explanation, demonstration, and supervised practice; or making recommendations on the basis of technical disciplines."@en . :peopleSupervising a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Supervising"@en ; :fjaDomain "people" ; :fjaRank 3 ; - :behavioralFacet "Management of Personnel Resources" , "Monitoring" , "Coordination" ; rdfs:comment "Determining or interpreting work procedures for a group of workers, assigning specific duties to them, maintaining harmonious relations among them, and promoting efficiency. A variety of responsibilities is involved in this function."@en . :peopleDiverting a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Diverting"@en ; :fjaDomain "people" ; :fjaRank 4 ; - :behavioralFacet "Speaking" ; rdfs:comment "Amusing others, usually through the medium of stage, screen, television, or radio."@en . :peoplePersuading a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Persuading"@en ; :fjaDomain "people" ; :fjaRank 5 ; - :behavioralFacet "Persuasion" , "Speaking" ; rdfs:comment "Influencing others in favor of a product, service, or point of view."@en . :peopleSpeakingSignalling a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Speaking-Signalling"@en ; :fjaDomain "people" ; :fjaRank 6 ; - :cognitiveFacet "Oral Comprehension" ; - :behavioralFacet "Speaking" ; rdfs:comment "Talking with and/or signalling people to convey or exchange information. Includes giving assignments and/or directions to helpers or assistants."@en . :peopleServing a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Serving"@en ; :fjaDomain "people" ; :fjaRank 7 ; - :affectiveFacet "Service Orientation" ; - :behavioralFacet "Active Listening" ; rdfs:comment "Attending to the needs or requests of people or animals or the expressed or implicit wishes of people. Immediate response is involved."@en . :peopleTakingInstructionsHelping a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Taking Instructions-Helping"@en ; :fjaDomain "people" ; :fjaRank 8 ; - :affectiveFacet "Cooperation" ; - :behavioralFacet "Active Listening" ; rdfs:comment "Attending to the work assignment instructions or orders of supervisor. (No immediate response required unless clarification of instructions or orders is needed.)"@en . # ---- Things (6th DOT digit): inanimate objects as defined by DOT ---- @@ -621,57 +580,46 @@ skos:inScheme :workerFunctionScheme ; skos:prefLabel "Setting Up"@en ; :fjaDomain "things" ; :fjaRank 0 ; - :behavioralFacet "Manual Dexterity" , "Equipment Maintenance" ; rdfs:comment "Preparing machines, equipment, or work stations for use by adjusting fixtures, controls, or settings according to job requirements."@en . :thingsPrecisionWorking a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Precision Working"@en ; :fjaDomain "things" ; :fjaRank 1 ; - :behavioralFacet "Control Precision" , "Arm-Hand Steadiness" ; rdfs:comment "Using body members and/or tools or work aids to work on, move, guide, or place objects or materials in situations where ultimate responsibility for the attainment of standards occurs and where the selection of appropriate tools, objects, or materials and the adjustment of the tool to the task require considerable tolerance for judgment."@en . :thingsOperatingControlling a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Operating-Controlling"@en ; :fjaDomain "things" ; :fjaRank 2 ; - :cognitiveFacet "Operation Monitoring" ; - :behavioralFacet "Rate Control" , "Reaction Time" ; rdfs:comment "Starting, stopping, and controlling the actions of machines and equipment by turning valves, switches, dials, buttons, levers, knobs, and similar devices, either singly or in combinations."@en . :thingsDrivingOperating a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Driving-Operating"@en ; :fjaDomain "things" ; :fjaRank 3 ; - :cognitiveFacet "Spatial Orientation" ; - :behavioralFacet "Reaction Time" , "Response Orientation" ; rdfs:comment "Starting, stopping, and controlling the actions of machines or vehicles, such as automobiles, trucks, tractors, buses, cranes, hoists, conveyors, and like equipment, that are operated primarily by steering or by manipulation of controls."@en . :thingsManipulating a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Manipulating"@en ; :fjaDomain "things" ; :fjaRank 4 ; - :behavioralFacet "Multilimb Coordination" , "Manual Dexterity" ; rdfs:comment "Using body members, handtools, and/or special devices to work on, move, guide, or place objects or materials."@en . :thingsTending a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Tending"@en ; :fjaDomain "things" ; :fjaRank 5 ; - :cognitiveFacet "Vigilance" ; - :behavioralFacet "Reaction Time" ; rdfs:comment "Starting, stopping, and observing the functioning of machines and equipment by manipulating controls according to a relatively fixed routine and/or by making adjustments when signaled or informed that the operation requires correction."@en . :thingsFeedingOffbearing a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Feeding-Offbearing"@en ; :fjaDomain "things" ; :fjaRank 6 ; - :behavioralFacet "Extent Flexibility" , "Static Strength" ; rdfs:comment "Inserting, throwing, dumping, or placing materials into or removing them from machines or equipment which are automatic or tended or operated by other workers."@en . :thingsHandling a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Handling"@en ; :fjaDomain "things" ; :fjaRank 7 ; - :behavioralFacet "Manual Dexterity" , "Finger Dexterity" ; rdfs:comment "Using body members, handtools, and/or special devices to install, remove, or otherwise handle materials or objects."@en . diff --git a/docs/product-requirements.md b/docs/product-requirements.md index f1f2fd3f7..cd99131a4 100644 --- a/docs/product-requirements.md +++ b/docs/product-requirements.md @@ -60,16 +60,15 @@ and rendered labels agree on term kind, direction, namespace, and provenance. - Publish the DOT/FJA Data/People/Things worker functions (24 concepts, official definitions verbatim) in the canonical ontology namespace - (ADR 0232), each with its definitional ordinal rank and qualitative - cognitive/affective/behavioral facet tags drawn only from published - Fleishman ability and O*NET skill/work-style vocabularies. + (ADR 0232), each with its official domain and definitional ordinal + rank. - Expose the taxonomy through a deterministic application read model with fail-closed lookups; an absent function is an honest unknown. - Carry no numeric weight from the taxonomy: ranks are scale positions, never calibrated weights. -Acceptance: completeness, verbatim definition, closed facet vocabulary, -deterministic ordering, and lookup round-trip isolation are enforced by +Acceptance: completeness, verbatim definition, deterministic ordering, +and lookup round-trip isolation are enforced by `tests/test_worker_function_taxonomy.py`; `tests/test_ontology.py` continues to pass unchanged. diff --git a/lineageweave/worker_function_taxonomy.py b/lineageweave/worker_function_taxonomy.py index 4bab0fbce..9e2d541e1 100644 --- a/lineageweave/worker_function_taxonomy.py +++ b/lineageweave/worker_function_taxonomy.py @@ -9,19 +9,13 @@ function. This module is the application-side projection of those concepts from `docs/ontology/lineageweave-kg.ttl`, where they live as a `skos:ConceptScheme` of `:WorkerFunction` concepts carrying the official -definitions, their definitional ordinal ranks, and qualitative -cognitive / affective / behavioral facet tags. +definitions and their definitional ordinal ranks. Provenance discipline mirrors the rest of this repository: - Ranks are scale positions copied from the published table -- never fitted, calibrated, or renormalized here. Nothing in this module may produce numeric weights (measurement stays governed by ADR 0145). -- Facet tags name published Fleishman ability families and O*NET basic / - cross-functional skills (Fleishman, Costanza, & Marshall-Mies, 1999; - Mumford, Peterson, & Childs, 1999) projected editorially onto each - official definition. They carry no weight and make no calibration - claim. - Lookups fail closed: an absent concept returns ``None``/empty rather than a placeholder, the same missing-vs-negative rule as the Null channels. @@ -59,15 +53,6 @@ #: 5th, Things the 6th -- used for deterministic sorting. _DOMAIN_ORDER: tuple[str, ...] = ("data", "people", "things") -#: Facet annotation groups read off each concept, mapped to the record -#: field names they populate. -_FACET_PROPERTIES: tuple[tuple[str, URIRef], ...] = ( - ("cognitive_facets", LW.cognitiveFacet), - ("affective_facets", LW.affectiveFacet), - ("behavioral_facets", LW.behavioralFacet), -) - - @dataclass(frozen=True) class WorkerFunctionRecord: """One worker-function concept exactly as the ontology declares it. @@ -94,34 +79,6 @@ class WorkerFunctionRecord: """The official DOT Appendix B definition, stored verbatim as the term's ``rdfs:comment``.""" - cognitive_facets: tuple[str, ...] - """Published Fleishman ability families or O*NET skills exercised - cognitively, alphabetically sorted for deterministic output.""" - - affective_facets: tuple[str, ...] - """Published O*NET work styles or social skills exercised - affectively, alphabetically sorted for deterministic output.""" - - behavioral_facets: tuple[str, ...] - """Published Fleishman psychomotor/physical abilities or O*NET - behavioral skills exercised behaviorally, alphabetically sorted for - deterministic output.""" - - -def _facet_values(subject: URIRef) -> dict[str, tuple[str, ...]]: - """Read the three facet annotation groups off one subject. - - Values are collected into sets first so duplicate annotations can - never surface twice, then returned as deterministically sorted - tuples keyed by record field name. - """ - values: dict[str, tuple[str, ...]] = {} - for field_name, predicate in _FACET_PROPERTIES: - seen = {str(value) for value in ONTOLOGY.objects(subject, predicate)} - values[field_name] = tuple(sorted(seen)) - return values - - def _record_for(subject: URIRef) -> WorkerFunctionRecord: """Build one record from its ontology subject. @@ -155,16 +112,12 @@ def _record_for(subject: URIRef) -> WorkerFunctionRecord: f"worker-function term {subject} declares unknown :fjaDomain " f"{domain!r}" ) - facets = _facet_values(subject) return WorkerFunctionRecord( iri=str(subject), domain=domain, rank=int(rank_literal), label=str(label_literal), definition=str(definition_literal), - cognitive_facets=facets["cognitive_facets"], - affective_facets=facets["affective_facets"], - behavioral_facets=facets["behavioral_facets"], ) @@ -202,21 +155,3 @@ def worker_function(domain: str, rank: int) -> WorkerFunctionRecord | None: if record.domain == domain and record.rank == rank: return record return None - - -def facets_for(domain: str, rank: int) -> dict[str, tuple[str, ...]]: - """The facet tag groups for one worker function. - - Returns the three sorted facet tuples for a declared function and - ``{}`` for an undeclared rank inside a valid domain -- the same - missing-vs-negative discipline as `ontology_annotations`. An - unrecognized ``domain`` raises ``ValueError``. - """ - record = worker_function(domain, rank) - if record is None: - return {} - return { - "cognitive_facets": record.cognitive_facets, - "affective_facets": record.affective_facets, - "behavioral_facets": record.behavioral_facets, - } diff --git a/tests/test_worker_function_taxonomy.py b/tests/test_worker_function_taxonomy.py index 29e2a9466..8a1b8677e 100644 --- a/tests/test_worker_function_taxonomy.py +++ b/tests/test_worker_function_taxonomy.py @@ -3,10 +3,9 @@ The tests treat the published Dictionary of Occupational Titles Appendix B tables as ground truth: every declared concept must carry -the official definition verbatim, the definitional rank from the -published table, and facet tags drawn only from the published Fleishman -ability / O*NET skill vocabularies. Nothing here may accept an invented -weight or a placeholder for missing evidence. +the official definition verbatim and the definitional rank from the +published table. Nothing here may accept an invented weight or a +placeholder for missing evidence. """ from __future__ import annotations @@ -17,7 +16,6 @@ from lineageweave.ontology import LW, ONTOLOGY, all_declared_lookup_codes from lineageweave.worker_function_taxonomy import ( WORKER_FUNCTION_DOMAINS, - facets_for, worker_function, worker_function_records, ) @@ -52,52 +50,6 @@ ("things", 7): "Using body members, handtools, and/or special devices to install", } -#: The closed facet-tag vocabulary this ADR allows: published Fleishman -#: ability families plus O*NET basic / cross-functional skill names. -_ALLOWED_FACET_TAGS = { - # Fleishman ability families (Fleishman et al., 1999) - "Arm-Hand Steadiness", - "Category Flexibility", - "Control Precision", - "Deductive Reasoning", - "Extent Flexibility", - "Finger Dexterity", - "Fluency of Ideas", - "Inductive Reasoning", - "Manual Dexterity", - "Mathematical Reasoning", - "Multilimb Coordination", - "Number Facility", - "Oral Comprehension", - "Oral Expression", - "Perceptual Speed", - "Rate Control", - "Reaction Time", - "Response Orientation", - "Spatial Orientation", - "Static Strength", - "Vigilance", - "Written Comprehension", - # O*NET basic / cross-functional skills and work styles - # (Mumford et al., 1999; Peterson et al., 1999) - "Active Listening", - "Cooperation", - "Coordination", - "Critical Thinking", - "Equipment Maintenance", - "Instructing", - "Management of Personnel Resources", - "Monitoring", - "Negotiation", - "Operation Monitoring", - "Persuasion", - "Service Orientation", - "Social Perceptiveness", - "Speaking", - "Time Management", -} - - def _record_map() -> dict[tuple[str, int], object]: """Index every declared record by its ``(domain, rank)`` pair.""" return {(record.domain, record.rank): record for record in worker_function_records()} @@ -138,31 +90,6 @@ def test_labels_are_unique_across_the_scheme() -> None: assert len(labels) == len(set(labels)) -def test_facet_tags_stay_inside_the_published_vocabularies() -> None: - """Every facet tag names a Fleishman ability family or O*NET skill - from the allowed set -- no invented construct may enter the layer.""" - for record in worker_function_records(): - all_tags = ( - *record.cognitive_facets, - *record.affective_facets, - *record.behavioral_facets, - ) - unknown = set(all_tags) - _ALLOWED_FACET_TAGS - assert not unknown, f"{(record.domain, record.rank)} invented facets: {unknown}" - - -def test_every_function_exercises_at_least_one_facet() -> None: - """Each definition projects onto at least one facet group; a fully - untagged function would mean the projection silently failed.""" - for record in worker_function_records(): - total = ( - len(record.cognitive_facets) - + len(record.affective_facets) - + len(record.behavioral_facets) - ) - assert total >= 1, f"{(record.domain, record.rank)} has no facets" - - def test_records_are_sorted_in_dot_digit_order_then_rank() -> None: """Output order is Data, People, Things with ascending ranks inside each domain -- byte-stable serialization input.""" @@ -221,27 +148,3 @@ def test_worker_function_raises_on_an_unknown_domain() -> None: """An unrecognized domain is caller error, not missing evidence.""" with pytest.raises(ValueError, match="unknown worker-function domain"): worker_function("machines", 0) - - -def test_facets_for_returns_groups_for_a_declared_pair() -> None: - """Facet lookup returns exactly the record's three sorted groups.""" - record = worker_function("data", 2) - assert record is not None - facets = facets_for("data", 2) - assert facets == { - "cognitive_facets": record.cognitive_facets, - "affective_facets": record.affective_facets, - "behavioral_facets": record.behavioral_facets, - } - assert facets["cognitive_facets"] == tuple(sorted(facets["cognitive_facets"])) - - -def test_facets_for_is_empty_for_an_undeclared_rank() -> None: - """Missing-vs-negative discipline mirrors ontology_annotations().""" - assert facets_for("things", 42) == {} - - -def test_facets_for_raises_on_an_unknown_domain() -> None: - """Domain validation propagates through the facet helper too.""" - with pytest.raises(ValueError, match="unknown worker-function domain"): - facets_for("vibes", 0) From c7c27889ed94fa9e04230705fe751e390b9718f6 Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 26 Aug 2026 21:49:14 +0900 Subject: [PATCH 04/12] fix(ontology): preserve authoritative worker-function semantics --- CHANGELOG.md | 6 +- ...tion-taxonomy-in-the-published-ontology.md | 50 +++++++++++----- .../WORKER_FUNCTION_TAXONOMY_REFERENCES.md | 27 +++++++-- docs/ontology/lineageweave-kg.ttl | 58 ++++++++++--------- docs/product-requirements.md | 6 +- lineageweave/worker_function_taxonomy.py | 11 ++-- scripts/build_ontology_site.py | 4 +- tests/test_worker_function_taxonomy.py | 56 +++++------------- 8 files changed, 118 insertions(+), 100 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f422ed9ec..b100f9fd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,8 +10,10 @@ All notable changes to this project are documented here. Format follows - The DOT/FJA Data/People/Things worker-function taxonomy is now published in the canonical ontology: all 24 worker functions carry the official - Dictionary of Occupational Titles Appendix B definitions verbatim and - their definitional ordinal ranks (ADR 0232). A deterministic application read + Dictionary of Occupational Titles Appendix B definitions verbatim, their + definitional ordinal ranks (ADR 0232). No DOT-to-O*NET or Fleishman + crosswalk is inferred without an authoritative mapping source. A + deterministic application read model (`lineageweave.worker_function_taxonomy`) exposes fail-closed lookups; ranks are scale positions and are never used as weights. - Persist explicit paragraph, list, table, MathML formula, and caller-parsed diff --git a/docs/adr/0232-worker-function-taxonomy-in-the-published-ontology.md b/docs/adr/0232-worker-function-taxonomy-in-the-published-ontology.md index ee2729421..258bc98c7 100644 --- a/docs/adr/0232-worker-function-taxonomy-in-the-published-ontology.md +++ b/docs/adr/0232-worker-function-taxonomy-in-the-published-ontology.md @@ -13,6 +13,11 @@ through three ordered lists, carried verbatim in the Dictionary of Occupational Titles Appendix B (U.S. Department of Labor, 1991): Data ranks 0-6, People ranks 0-8, Things ranks 0-7, each ordered so that the lower digit names the more complex function (Fine & Cronshaw, 1999). +The cited Fleishman and O*NET sources define separate ability, skill, +and work-style taxonomies, but do not publish a crosswalk from these 24 +DOT worker functions. Mapping them locally would be an unsupported +semantic assertion. + The repository had no representation for this vocabulary: a post or analysis cannot yet say "this evidence describes Analyzing-level data work" without inventing an untracked term. The ontology is the correct @@ -30,8 +35,9 @@ PostgreSQL graph facts -- but two constraints bind any addition: 1. Publish all 24 worker functions as a `skos:ConceptScheme` (`:workerFunctionScheme`) of `:WorkerFunction` concepts in `docs/ontology/lineageweave-kg.ttl`, each carrying the official DOT - Appendix B definition verbatim as its `rdfs:comment`, its definitional - rank (`:fjaRank`) and its domain (`:fjaDomain`). + Appendix B definition verbatim as its `skos:definition`, its definitional + rank (`:fjaRank`), and its domain (`:fjaDomain`). No DOT-to-O*NET or + Fleishman crosswalk is asserted without an authoritative mapping source. 2. Like column-projection datatype properties, these concepts carry no `:lookupCode`: they are not `common_lookup_value` rows, so the round trip is untouched. Binding a function to stored rows needs a separate @@ -47,33 +53,25 @@ PostgreSQL graph facts -- but two constraints bind any addition: malformed TTL declaration. 5. The canonical repository-case namespace (ADR 0207) mints every IRI; no lowercase compatibility form is introduced. -6. Do not publish cognitive, affective, or behavioral crosswalks from - the DOT/FJA functions to Fleishman or O*NET constructs unless an - authoritative source publishes the exact term-level mapping. The - initial branch's `:cognitiveFacet`, `:affectiveFacet`, and - `:behavioralFacet` projection was editorial rather than sourced, so - it is removed before delivery under the repository's no-heuristic - and provenance-preservation contracts. ## Consequences - The IO-psychology worker-function vocabulary becomes addressable and citable inside the published semantic layer before any persistence decision exists. -- The ontology does not imply an unsupported ability, skill, work-style, - or behavioral requirement from the official function definitions. +- A future crosswalk requires its own provenance-bearing decision and an + authoritative mapping source; shared labels alone are not such evidence. - Adding DB-backed function annotations later means one migration plus seed rows and `:lookupCode` declarations -- the extension path is additive by construction. -- `tests/test_worker_function_taxonomy.py` pins the published text with - verbatim definition prefixes, so definition drift fails CI. +- `tests/test_worker_function_taxonomy.py` pins the complete published text, + so truncation, paraphrase, and spelling drift fail CI. ## Verification - `tests/test_worker_function_taxonomy.py`: completeness (24 concepts), - per-domain rank extents, verbatim official-definition prefixes, - deterministic ordering, canonical namespace, - fail-closed lookups. + per-domain rank extents, complete verbatim official definitions, + deterministic ordering, canonical namespace, fail-closed lookups. - `tests/test_ontology.py` continues to pass unchanged: the round trip sees no new lookup codes. @@ -83,6 +81,26 @@ Fine, S. A., & Cronshaw, S. F. (1999). *Functional job analysis: A foundation for human resources management*. Lawrence Erlbaum Associates. +Fleishman, E. A., & Quaintance, M. K. (1984). *Taxonomies of human +performance: The description of human tasks*. Academic Press. + +Fleishman, E. A., Costanza, D. P., & Marshall-Mies, J. C. (1999). +Abilities. In N. G. Peterson, M. D. Mumford, W. C. Borman, P. R. +Jeanneret, & E. A. Fleishman (Eds.), *An occupational information system +for the 21st century: The development of O*NET* (pp. 97-112). American +Psychological Association. + +Mumford, M. D., Peterson, N. G., & Childs, R. A. (1999). Basic and +cross-functional skills. In N. G. Peterson, M. D. Mumford, W. C. Borman, +P. R. Jeanneret, & E. A. Fleishman (Eds.), *An occupational information +system for the 21st century: The development of O*NET* (pp. 49-69). +American Psychological Association. + +Peterson, N. G., Mumford, M. D., Borman, W. C., Jeanneret, P. R., & +Fleishman, E. A. (Eds.). (1999). *An occupational information system for +the 21st century: The development of O\*NET*. American Psychological +Association. + U.S. Department of Labor. (1991). *Dictionary of occupational titles* (4th ed., rev., Appendix B). U.S. Government Printing Office. https://www.dol.gov/agencies/oalj/PUBLIC/DOT/REFERENCES/DOTAPPB diff --git a/docs/doctoring/WORKER_FUNCTION_TAXONOMY_REFERENCES.md b/docs/doctoring/WORKER_FUNCTION_TAXONOMY_REFERENCES.md index 8abf59e11..b23412068 100644 --- a/docs/doctoring/WORKER_FUNCTION_TAXONOMY_REFERENCES.md +++ b/docs/doctoring/WORKER_FUNCTION_TAXONOMY_REFERENCES.md @@ -3,12 +3,32 @@ Supporting literature for [ADR 0232](../adr/0232-worker-function-taxonomy-in-the-published-ontology.md). APA 7th edition. This list is the citation record for the DOT/FJA -Data/People/Things worker functions and their official definitions and -rank positions. +Data/People/Things worker functions. The cited Fleishman and O*NET sources +do not establish a crosswalk from those functions. Fine, S. A., & Cronshaw, S. F. (1999). *Functional job analysis: A foundation for human resources management*. Lawrence Erlbaum Associates. +Fleishman, E. A., & Quaintance, M. K. (1984). *Taxonomies of human +performance: The description of human tasks*. Academic Press. + +Fleishman, E. A., Costanza, D. P., & Marshall-Mies, J. C. (1999). +Abilities. In N. G. Peterson, M. D. Mumford, W. C. Borman, P. R. +Jeanneret, & E. A. Fleishman (Eds.), *An occupational information system +for the 21st century: The development of O\*NET* (pp. 97–112). American +Psychological Association. + +Mumford, M. D., Peterson, N. G., & Childs, R. A. (1999). Basic and +cross-functional skills. In N. G. Peterson, M. D. Mumford, W. C. Borman, +P. R. Jeanneret, & E. A. Fleishman (Eds.), *An occupational information +system for the 21st century: The development of O\*NET* (pp. 49–69). +American Psychological Association. + +Peterson, N. G., Mumford, M. D., Borman, W. C., Jeanneret, P. R., & +Fleishman, E. A. (Eds.). (1999). *An occupational information system for +the 21st century: The development of O\*NET*. American Psychological +Association. + U.S. Department of Labor. (1991). *Dictionary of occupational titles* (4th ed., rev., Appendix B). U.S. Government Printing Office. https://www.dol.gov/agencies/oalj/PUBLIC/DOT/REFERENCES/DOTAPPB @@ -27,6 +47,3 @@ DOT table; they are definitional scale positions and never fitted or calibrated weights. Score interpretation and fairness remain governed by AERA, APA, and NCME (2014), cited in the repository's measurement-boundary ADRs ([ADR 0145](../adr/0145-psychometric-channel-weight-estimation.md)). -No authoritative source in this register publishes an exact term-level -crosswalk from the 24 DOT/FJA functions to Fleishman or O*NET constructs; -therefore this ontology does not publish one (ADR 0232). diff --git a/docs/ontology/lineageweave-kg.ttl b/docs/ontology/lineageweave-kg.ttl index 6fede4ab6..59c33ce8c 100644 --- a/docs/ontology/lineageweave-kg.ttl +++ b/docs/ontology/lineageweave-kg.ttl @@ -443,10 +443,12 @@ # The Dictionary of Occupational Titles' Data/People/Things worker # functions (U.S. Department of Labor, 1991, Appendix B) descend from # Functional Job Analysis (Fine & Cronshaw, 1999). Each function below -# carries the official DOT definition verbatim as its rdfs:comment, +# carries the official DOT definition verbatim as its skos:definition, # its definitional ordinal rank (:fjaRank -- lower digits denote the # more complex function; these are scale positions, never fitted or -# calibrated weights). Channel-weight estimation stays governed by ADR 0145. +# calibrated weights). Channel-weight estimation stays governed by +# ADR 0145. No DOT-to-O*NET or Fleishman crosswalk is asserted because +# the cited authorities do not publish one. # # Like column-projection properties above, these concepts deliberately # do NOT carry :lookupCode: they are not common_lookup_value rows, so @@ -480,43 +482,43 @@ skos:inScheme :workerFunctionScheme ; skos:prefLabel "Synthesizing"@en ; :fjaDomain "data" ; :fjaRank 0 ; - rdfs:comment "Integrating analyses of data to discover facts and/or develop knowledge concepts or interpretations."@en . + skos:definition "Integrating analyses of data to discover facts and/or develop knowledge concepts or interpretations."@en . :dataCoordinating a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Coordinating"@en ; :fjaDomain "data" ; :fjaRank 1 ; - rdfs:comment "Determining time, place, and sequence of operations or action to be taken on the basis of analysis of data; executing determinations and/or reporting on events."@en . + skos:definition "Determining time, place, and sequence of operations or action to be taken on the basis of analysis of data; executing determinations and/or reporting on events."@en . :dataAnalyzing a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Analyzing"@en ; :fjaDomain "data" ; :fjaRank 2 ; - rdfs:comment "Examining and evaluating data. Presenting alternative actions in relation to the evaluation is frequently involved."@en . + skos:definition "Examining and evaluating data. Presenting alternative actions in relation to the evaluation is frequently involved."@en . :dataCompiling a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Compiling"@en ; :fjaDomain "data" ; :fjaRank 3 ; - rdfs:comment "Gathering, collating, or classifying information about data, people, or things. Reporting and/or carrying out a prescribed action in relation to the information is frequently involved."@en . + skos:definition "Gathering, collating, or classifying information about data, people, or things. Reporting and/or carrying out a prescribed action in relation to the information is frequently involved."@en . :dataComputing a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Computing"@en ; :fjaDomain "data" ; :fjaRank 4 ; - rdfs:comment "Performing arithmetic operations and reporting on and/or carrying out a prescribed action in relation to them. Does not include counting."@en . + skos:definition "Performing arithmetic operations and reporting on and/or carrying out a prescribed action in relation to them. Does not include counting."@en . :dataCopying a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Copying"@en ; :fjaDomain "data" ; :fjaRank 5 ; - rdfs:comment "Transcribing, entering, or posting data."@en . + skos:definition "Transcribing, entering, or posting data."@en . :dataComparing a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Comparing"@en ; :fjaDomain "data" ; :fjaRank 6 ; - rdfs:comment "Judging the readily observable functional, structural, or compositional characteristics (whether similar to or divergent from obvious standards) of data, people, or things."@en . + skos:definition "Judging the readily observable functional, structural, or compositional characteristics (whether similar to or divergent from obvious standards) of data, people, or things."@en . # ---- People (5th DOT digit): human beings dealt with individually ---- @@ -524,55 +526,55 @@ skos:inScheme :workerFunctionScheme ; skos:prefLabel "Mentoring"@en ; :fjaDomain "people" ; :fjaRank 0 ; - rdfs:comment "Dealing with individuals in terms of their total personality in order to advise, counsel, and/or guide them with regard to problems that may be resolved by legal, scientific, clinical, spiritual, and/or other professional principles."@en . + skos:definition "Dealing with individuals in terms of their total personality in order to advise, counsel, and/or guide them with regard to problems that may be resolved by legal, scientific, clinical, spiritual, and/or other professional principles."@en . :peopleNegotiating a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Negotiating"@en ; :fjaDomain "people" ; :fjaRank 1 ; - rdfs:comment "Exchanging ideas, information, and opinions with others to formulate policies and programs and/or arrive jointly at decisions, conclusions, or solutions."@en . + skos:definition "Exchanging ideas, information, and opinions with others to formulate policies and programs and/or arrive jointly at decisions, conclusions, or solutions."@en . :peopleInstructing a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Instructing"@en ; :fjaDomain "people" ; :fjaRank 2 ; - rdfs:comment "Teaching subject matter to others, or training others (including animals) through explanation, demonstration, and supervised practice; or making recommendations on the basis of technical disciplines."@en . + skos:definition "Teaching subject matter to others, or training others (including animals) through explanation, demonstration, and supervised practice; or making recommendations on the basis of technical disciplines."@en . :peopleSupervising a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Supervising"@en ; :fjaDomain "people" ; :fjaRank 3 ; - rdfs:comment "Determining or interpreting work procedures for a group of workers, assigning specific duties to them, maintaining harmonious relations among them, and promoting efficiency. A variety of responsibilities is involved in this function."@en . + skos:definition "Determining or interpreting work procedures for a group of workers, assigning specific duties to them, maintaining harmonious relations among them, and promoting efficiency. A variety of responsibilities is involved in this function."@en . :peopleDiverting a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Diverting"@en ; :fjaDomain "people" ; :fjaRank 4 ; - rdfs:comment "Amusing others, usually through the medium of stage, screen, television, or radio."@en . + skos:definition "Amusing others, usually through the medium of stage, screen, television, or radio."@en . :peoplePersuading a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Persuading"@en ; :fjaDomain "people" ; :fjaRank 5 ; - rdfs:comment "Influencing others in favor of a product, service, or point of view."@en . + skos:definition "Influencing others in favor of a product, service, or point of view."@en . -:peopleSpeakingSignalling a skos:Concept , :WorkerFunction ; +:peopleSpeakingSignaling a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; - skos:prefLabel "Speaking-Signalling"@en ; + skos:prefLabel "Speaking-Signaling"@en ; :fjaDomain "people" ; :fjaRank 6 ; - rdfs:comment "Talking with and/or signalling people to convey or exchange information. Includes giving assignments and/or directions to helpers or assistants."@en . + skos:definition "Talking with and/or signaling people to convey or exchange information. Includes giving assignments and/or directions to helpers or assistants."@en . :peopleServing a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Serving"@en ; :fjaDomain "people" ; :fjaRank 7 ; - rdfs:comment "Attending to the needs or requests of people or animals or the expressed or implicit wishes of people. Immediate response is involved."@en . + skos:definition "Attending to the needs or requests of people or animals or the expressed or implicit wishes of people. Immediate response is involved."@en . :peopleTakingInstructionsHelping a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Taking Instructions-Helping"@en ; :fjaDomain "people" ; :fjaRank 8 ; - rdfs:comment "Attending to the work assignment instructions or orders of supervisor. (No immediate response required unless clarification of instructions or orders is needed.)"@en . + skos:definition "Attending to the work assignment instructions or orders of supervisor. (No immediate response required unless clarification of instructions or orders is needed.) Helping applies to 'non-learning' helpers."@en . # ---- Things (6th DOT digit): inanimate objects as defined by DOT ---- @@ -580,46 +582,46 @@ skos:inScheme :workerFunctionScheme ; skos:prefLabel "Setting Up"@en ; :fjaDomain "things" ; :fjaRank 0 ; - rdfs:comment "Preparing machines, equipment, or work stations for use by adjusting fixtures, controls, or settings according to job requirements."@en . + skos:definition "Preparing machines (or equipment) for operation by planning order of successive machine operations, installing and adjusting tools and other machine components, adjusting the position of workpiece or material, setting controls, and verifying accuracy of machine capabilities, properties of materials, and shop practices. Uses tools, equipment, and work aids, such as precision gauges and measuring instruments. Workers who set up one or a number of machines for other workers or who set up and personally operate a variety of machines are included here."@en . :thingsPrecisionWorking a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Precision Working"@en ; :fjaDomain "things" ; :fjaRank 1 ; - rdfs:comment "Using body members and/or tools or work aids to work on, move, guide, or place objects or materials in situations where ultimate responsibility for the attainment of standards occurs and where the selection of appropriate tools, objects, or materials and the adjustment of the tool to the task require considerable tolerance for judgment."@en . + skos:definition "Using body members and/or tools or work aids to work, move, guide, or place objects or materials in situations where ultimate responsibility for the attainment of standards occurs and selection of appropriate tools, objects, or materials, and the adjustment of the tool to the task require exercise of considerable judgment."@en . :thingsOperatingControlling a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Operating-Controlling"@en ; :fjaDomain "things" ; :fjaRank 2 ; - rdfs:comment "Starting, stopping, and controlling the actions of machines and equipment by turning valves, switches, dials, buttons, levers, knobs, and similar devices, either singly or in combinations."@en . + skos:definition "Starting, stopping, controlling, and adjusting the progress of machines or equipment. Operating machines involves setting up and adjusting the machine or material(s) as the work progresses. Controlling involves observing gauges, dials, etc., and turning valves and other devices to regulate factors such as temperature, pressure, flow of liquids, speed of pumps, and reactions of materials."@en . :thingsDrivingOperating a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Driving-Operating"@en ; :fjaDomain "things" ; :fjaRank 3 ; - rdfs:comment "Starting, stopping, and controlling the actions of machines or vehicles, such as automobiles, trucks, tractors, buses, cranes, hoists, conveyors, and like equipment, that are operated primarily by steering or by manipulation of controls."@en . + skos:definition "Starting, stopping, and controlling the actions of machines or equipment for which a course must be steered or which must be guided to control the movement of things or people for a variety of purposes. Involves such activities as observing gauges and dials, estimating distances and determining speed and direction of other objects, turning cranks and wheels, and pushing or pulling gear lifts or levers. Includes such machines as cranes, conveyor systems, tractors, furnace-charging machines, paving machines, and hoisting machines. Excludes manually powered machines, such as handtrucks and dollies, and power-assisted machines, such as electric wheelbarrows and handtrucks."@en . :thingsManipulating a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Manipulating"@en ; :fjaDomain "things" ; :fjaRank 4 ; - rdfs:comment "Using body members, handtools, and/or special devices to work on, move, guide, or place objects or materials."@en . + skos:definition "Using body members, tools, or special devices to work, move, guide, or place objects or materials. Involves some latitude for judgment with regard to precision attained and selecting appropriate tool, object, or material, although this is readily manifest."@en . :thingsTending a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Tending"@en ; :fjaDomain "things" ; :fjaRank 5 ; - rdfs:comment "Starting, stopping, and observing the functioning of machines and equipment by manipulating controls according to a relatively fixed routine and/or by making adjustments when signaled or informed that the operation requires correction."@en . + skos:definition "Starting, stopping, and observing the functioning of machines and equipment. Involves adjusting materials or controls of the machine, such as changing guides, adjusting timers and temperature gauges, turning valves to allow flow of materials, and flipping switches in response to lights. Little judgment is involved in making these adjustments."@en . :thingsFeedingOffbearing a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Feeding-Offbearing"@en ; :fjaDomain "things" ; :fjaRank 6 ; - rdfs:comment "Inserting, throwing, dumping, or placing materials into or removing them from machines or equipment which are automatic or tended or operated by other workers."@en . + skos:definition "Inserting, throwing, dumping, or placing materials in or removing them from machines or equipment which are automatic or tended or operated by other workers."@en . :thingsHandling a skos:Concept , :WorkerFunction ; skos:inScheme :workerFunctionScheme ; skos:prefLabel "Handling"@en ; :fjaDomain "things" ; :fjaRank 7 ; - rdfs:comment "Using body members, handtools, and/or special devices to install, remove, or otherwise handle materials or objects."@en . + skos:definition "Using body members, handtools, and/or special devices to work, move, or carry objects or materials. Involves little or no latitude for judgment with regard to attainment of standards or in selecting appropriate tool, object, or materials."@en . diff --git a/docs/product-requirements.md b/docs/product-requirements.md index cd99131a4..b728080b1 100644 --- a/docs/product-requirements.md +++ b/docs/product-requirements.md @@ -60,14 +60,14 @@ and rendered labels agree on term kind, direction, namespace, and provenance. - Publish the DOT/FJA Data/People/Things worker functions (24 concepts, official definitions verbatim) in the canonical ontology namespace - (ADR 0232), each with its official domain and definitional ordinal - rank. + (ADR 0232), each with its definitional ordinal rank. Do not infer a + DOT-to-O*NET or Fleishman crosswalk that the authorities do not publish. - Expose the taxonomy through a deterministic application read model with fail-closed lookups; an absent function is an honest unknown. - Carry no numeric weight from the taxonomy: ranks are scale positions, never calibrated weights. -Acceptance: completeness, verbatim definition, deterministic ordering, +Acceptance: completeness, full verbatim definitions, deterministic ordering, and lookup round-trip isolation are enforced by `tests/test_worker_function_taxonomy.py`; `tests/test_ontology.py` continues to pass unchanged. diff --git a/lineageweave/worker_function_taxonomy.py b/lineageweave/worker_function_taxonomy.py index 9e2d541e1..ccd91fd9c 100644 --- a/lineageweave/worker_function_taxonomy.py +++ b/lineageweave/worker_function_taxonomy.py @@ -16,6 +16,8 @@ - Ranks are scale positions copied from the published table -- never fitted, calibrated, or renormalized here. Nothing in this module may produce numeric weights (measurement stays governed by ADR 0145). +- No DOT-to-O*NET or Fleishman crosswalk is inferred: the cited + authorities do not publish one. - Lookups fail closed: an absent concept returns ``None``/empty rather than a placeholder, the same missing-vs-negative rule as the Null channels. @@ -36,7 +38,7 @@ from functools import lru_cache from rdflib import URIRef -from rdflib.namespace import RDFS, SKOS +from rdflib.namespace import SKOS from .ontology import LW, ONTOLOGY @@ -77,7 +79,8 @@ class WorkerFunctionRecord: definition: str """The official DOT Appendix B definition, stored verbatim as the - term's ``rdfs:comment``.""" + term's ``skos:definition``.""" + def _record_for(subject: URIRef) -> WorkerFunctionRecord: """Build one record from its ontology subject. @@ -90,14 +93,14 @@ def _record_for(subject: URIRef) -> WorkerFunctionRecord: domain_literal = ONTOLOGY.value(subject, LW.fjaDomain) rank_literal = ONTOLOGY.value(subject, LW.fjaRank) label_literal = ONTOLOGY.value(subject, SKOS.prefLabel) - definition_literal = ONTOLOGY.value(subject, RDFS.comment) + definition_literal = ONTOLOGY.value(subject, SKOS.definition) missing = [ name for name, literal in ( (":fjaDomain", domain_literal), (":fjaRank", rank_literal), ("skos:prefLabel", label_literal), - ("rdfs:comment", definition_literal), + ("skos:definition", definition_literal), ) if literal is None ] diff --git a/scripts/build_ontology_site.py b/scripts/build_ontology_site.py index 62eab1a7c..5607c7458 100644 --- a/scripts/build_ontology_site.py +++ b/scripts/build_ontology_site.py @@ -162,7 +162,9 @@ def _render_term(graph: Graph, subject: URIRef, ontology_subjects: set[URIRef]) or _preferred_literal(graph, subject, SKOS.prefLabel) or raw_fragment ) - comment = _preferred_literal(graph, subject, RDFS.comment) + comment = _preferred_literal(graph, subject, SKOS.definition) or _preferred_literal( + graph, subject, RDFS.comment + ) lookup_predicate = URIRef(CANONICAL_LOOKUP_PREDICATE) lookup_codes = sorted(str(value) for value in graph.objects(subject, lookup_predicate)) type_values = sorted( diff --git a/tests/test_worker_function_taxonomy.py b/tests/test_worker_function_taxonomy.py index 8a1b8677e..86bc73988 100644 --- a/tests/test_worker_function_taxonomy.py +++ b/tests/test_worker_function_taxonomy.py @@ -3,13 +3,15 @@ The tests treat the published Dictionary of Occupational Titles Appendix B tables as ground truth: every declared concept must carry -the official definition verbatim and the definitional rank from the -published table. Nothing here may accept an invented weight or a -placeholder for missing evidence. +the official definition verbatim, the definitional rank from the +published table. Nothing here may accept an invented weight, unsupported +crosswalk, or placeholder for missing evidence. """ from __future__ import annotations +import hashlib + import pytest from rdflib import RDF @@ -20,35 +22,9 @@ worker_function_records, ) -#: Verbatim opening fragments of official DOT Appendix B definitions, -#: keyed by ``(domain, rank)`` -- a real-world accuracy check that the -#: ontology carries the published text rather than a paraphrase. -_OFFICIAL_DEFINITION_PREFIXES: dict[tuple[str, int], str] = { - ("data", 0): "Integrating analyses of data to discover facts", - ("data", 1): "Determining time, place, and sequence of operations", - ("data", 2): "Examining and evaluating data.", - ("data", 3): "Gathering, collating, or classifying information about data", - ("data", 4): "Performing arithmetic operations", - ("data", 5): "Transcribing, entering, or posting data.", - ("data", 6): "Judging the readily observable functional, structural", - ("people", 0): "Dealing with individuals in terms of their total personality", - ("people", 1): "Exchanging ideas, information, and opinions with others", - ("people", 2): "Teaching subject matter to others", - ("people", 3): "Determining or interpreting work procedures", - ("people", 4): "Amusing others", - ("people", 5): "Influencing others in favor of a product, service", - ("people", 6): "Talking with and/or signalling people", - ("people", 7): "Attending to the needs or requests of people", - ("people", 8): "Attending to the work assignment instructions or orders", - ("things", 0): "Preparing machines, equipment, or work stations", - ("things", 1): "Using body members and/or tools or work aids to work on", - ("things", 2): "Starting, stopping, and controlling the actions of machines and equipment", - ("things", 3): "Starting, stopping, and controlling the actions of machines or vehicles", - ("things", 4): "Using body members, handtools, and/or special devices", - ("things", 5): "Starting, stopping, and observing the functioning of machines", - ("things", 6): "Inserting, throwing, dumping, or placing materials", - ("things", 7): "Using body members, handtools, and/or special devices to install", -} +_OFFICIAL_TAXONOMY_SHA256 = ( + "b960c338f8fa6a2795dc402a527b012bfadc24de45a1333845c05531e7c32ba3" +) def _record_map() -> dict[tuple[str, int], object]: """Index every declared record by its ``(domain, rank)`` pair.""" @@ -72,15 +48,12 @@ def test_domain_ranks_match_the_published_table_extents() -> None: def test_definitions_carry_the_official_dot_text() -> None: - """Every concept's comment is the official Appendix B definition, - verified against verbatim opening fragments.""" - indexed = _record_map() - for key, prefix in _OFFICIAL_DEFINITION_PREFIXES.items(): - record = indexed[key] - assert record.definition.startswith(prefix), ( - f"{key} definition drifted from the official text: " - f"{record.definition!r}" - ) + """The complete ordered taxonomy matches the verified DOT text.""" + payload = "\n".join( + f"{record.domain}:{record.rank}:{record.label}:{record.definition}" + for record in worker_function_records() + ) + assert hashlib.sha256(payload.encode()).hexdigest() == _OFFICIAL_TAXONOMY_SHA256 def test_labels_are_unique_across_the_scheme() -> None: @@ -126,6 +99,7 @@ def test_worker_functions_carry_no_lookup_code() -> None: [ ("data", 0, "Synthesizing"), ("people", 0, "Mentoring"), + ("people", 6, "Speaking-Signaling"), ("people", 8, "Taking Instructions-Helping"), ("things", 0, "Setting Up"), ("things", 7, "Handling"), From ec9a1353f0e04cc009386fb9089355aa1f0d334e Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 26 Aug 2026 23:00:09 +0900 Subject: [PATCH 05/12] test: remove unused taxonomy imports --- tests/test_worker_function_taxonomy.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/test_worker_function_taxonomy.py b/tests/test_worker_function_taxonomy.py index 86bc73988..374a724e3 100644 --- a/tests/test_worker_function_taxonomy.py +++ b/tests/test_worker_function_taxonomy.py @@ -13,9 +13,8 @@ import hashlib import pytest -from rdflib import RDF -from lineageweave.ontology import LW, ONTOLOGY, all_declared_lookup_codes +from lineageweave.ontology import all_declared_lookup_codes from lineageweave.worker_function_taxonomy import ( WORKER_FUNCTION_DOMAINS, worker_function, @@ -69,9 +68,9 @@ def test_records_are_sorted_in_dot_digit_order_then_rank() -> None: records = worker_function_records() domain_order = ["data"] * 7 + ["people"] * 9 + ["things"] * 8 assert [record.domain for record in records] == domain_order - assert [record.rank for record in records[:7]] == list(range(0, 7)) - assert [record.rank for record in records[7:16]] == list(range(0, 9)) - assert [record.rank for record in records[16:]] == list(range(0, 8)) + assert [record.rank for record in records[:7]] == list(range(7)) + assert [record.rank for record in records[7:16]] == list(range(9)) + assert [record.rank for record in records[16:]] == list(range(8)) def test_iri_is_the_canonical_repository_case_namespace() -> None: From 8ef4090cea1fc77de910ea066870c2b57dbe588e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 27 Aug 2026 00:50:35 +0900 Subject: [PATCH 06/12] fix(ontology): validate and display FJA coordinates --- ...tion-taxonomy-in-the-published-ontology.md | 4 +- lineageweave/worker_function_taxonomy.py | 53 ++++++++++++------- scripts/build_ontology_site.py | 15 ++++++ tests/test_ontology_site.py | 15 ++++++ tests/test_worker_function_taxonomy.py | 31 ++++++++++- 5 files changed, 97 insertions(+), 21 deletions(-) diff --git a/docs/adr/0232-worker-function-taxonomy-in-the-published-ontology.md b/docs/adr/0232-worker-function-taxonomy-in-the-published-ontology.md index 258bc98c7..64cbcc4f0 100644 --- a/docs/adr/0232-worker-function-taxonomy-in-the-published-ontology.md +++ b/docs/adr/0232-worker-function-taxonomy-in-the-published-ontology.md @@ -1,7 +1,7 @@ # ADR 0232: Worker-function taxonomy in the published ontology -**Status:** Accepted -**Date:** 2026-08-26 +**Status:** Accepted +**Date:** 2026-08-26 **Extends:** [ADR 0004](0004-knowledge-graph-ontology.md), [ADR 0145](0145-psychometric-channel-weight-estimation.md), [ADR 0207](0207-repository-case-ontology-namespace-canonical.md) ## Context diff --git a/lineageweave/worker_function_taxonomy.py b/lineageweave/worker_function_taxonomy.py index ccd91fd9c..3218d2203 100644 --- a/lineageweave/worker_function_taxonomy.py +++ b/lineageweave/worker_function_taxonomy.py @@ -38,7 +38,7 @@ from functools import lru_cache from rdflib import URIRef -from rdflib.namespace import SKOS +from rdflib.namespace import RDF, SKOS from .ontology import LW, ONTOLOGY @@ -55,6 +55,7 @@ #: 5th, Things the 6th -- used for deterministic sorting. _DOMAIN_ORDER: tuple[str, ...] = ("data", "people", "things") + @dataclass(frozen=True) class WorkerFunctionRecord: """One worker-function concept exactly as the ontology declares it. @@ -90,35 +91,48 @@ def _record_for(subject: URIRef) -> WorkerFunctionRecord: malformed declaration must surface loudly rather than degrade into an invented default, matching the repository's fail-closed rule. """ - domain_literal = ONTOLOGY.value(subject, LW.fjaDomain) - rank_literal = ONTOLOGY.value(subject, LW.fjaRank) - label_literal = ONTOLOGY.value(subject, SKOS.prefLabel) - definition_literal = ONTOLOGY.value(subject, SKOS.definition) - missing = [ - name - for name, literal in ( - (":fjaDomain", domain_literal), - (":fjaRank", rank_literal), - ("skos:prefLabel", label_literal), - ("skos:definition", definition_literal), + if (subject, RDF.type, LW.WorkerFunction) not in ONTOLOGY or not str(subject).startswith( + str(LW) + ): + raise ValueError(f"worker-function term {subject} has invalid type or namespace") + values = { + name: tuple(ONTOLOGY.objects(subject, predicate)) + for name, predicate in ( + (":fjaDomain", LW.fjaDomain), + (":fjaRank", LW.fjaRank), + ("skos:prefLabel", SKOS.prefLabel), + ("skos:definition", SKOS.definition), ) - if literal is None - ] - if missing: + } + invalid = [name for name, declared in values.items() if len(declared) != 1] + if invalid: raise ValueError( - f"worker-function term {subject} is missing required " - f"annotations: {', '.join(missing)}" + f"worker-function term {subject} must declare exactly one of: " + f"{', '.join(invalid)}" ) + domain_literal, rank_literal, label_literal, definition_literal = ( + values[name][0] + for name in (":fjaDomain", ":fjaRank", "skos:prefLabel", "skos:definition") + ) domain = str(domain_literal) if domain not in WORKER_FUNCTION_DOMAINS: raise ValueError( f"worker-function term {subject} declares unknown :fjaDomain " f"{domain!r}" ) + try: + rank = int(rank_literal) + except (TypeError, ValueError) as exc: + raise ValueError(f"worker-function term {subject} has invalid :fjaRank") from exc + low, high = WORKER_FUNCTION_DOMAINS[domain] + if rank not in range(low, high + 1): + raise ValueError( + f"worker-function term {subject} declares out-of-range :fjaRank {rank}" + ) return WorkerFunctionRecord( iri=str(subject), domain=domain, - rank=int(rank_literal), + rank=rank, label=str(label_literal), definition=str(definition_literal), ) @@ -137,6 +151,9 @@ def worker_function_records() -> tuple[WorkerFunctionRecord, ...]: _record_for(subject) for subject in ONTOLOGY.subjects(SKOS.inScheme, LW.workerFunctionScheme) ] + keys = {(record.domain, record.rank) for record in records} + if len(keys) != len(records): + raise ValueError("worker-function terms declare a duplicate domain/rank pair") domain_index = {name: index for index, name in enumerate(_DOMAIN_ORDER)} records.sort(key=lambda record: (domain_index[record.domain], record.rank)) return tuple(records) diff --git a/scripts/build_ontology_site.py b/scripts/build_ontology_site.py index 5607c7458..497b3757c 100644 --- a/scripts/build_ontology_site.py +++ b/scripts/build_ontology_site.py @@ -34,6 +34,12 @@ CANONICAL_LOOKUP_PREDICATE = ( "https://contextualwisdomlab.github.io/LineageWeave/ontology#lookupCode" ) +CANONICAL_FJA_DOMAIN_PREDICATE = URIRef( + "https://contextualwisdomlab.github.io/LineageWeave/ontology#fjaDomain" +) +CANONICAL_FJA_RANK_PREDICATE = URIRef( + "https://contextualwisdomlab.github.io/LineageWeave/ontology#fjaRank" +) SHAPES_RELATIVE_PATH = Path("docs/ontology/lineageweave-kg-shapes.ttl") CANONICAL_LINK_SUPPRESSION = ( "