diff --git a/.update_readmes/20260314_131842__085-didactopus-workspace-manager-update__README.md b/.update_readmes/20260314_131842__085-didactopus-workspace-manager-update__README.md new file mode 100644 index 0000000..f1fd76b --- /dev/null +++ b/.update_readmes/20260314_131842__085-didactopus-workspace-manager-update__README.md @@ -0,0 +1,28 @@ +# Didactopus + +Didactopus is a local-first AI-assisted autodidactic mastery platform built around +concept graphs, evaluator-driven evidence, adaptive planning, mastery ledgers, +curriculum ingestion, and human review of generated draft packs. + +## This revision + +This revision adds a **workspace manager** on top of the local review bridge. + +The goal is to reduce the remaining friction in getting from: +- raw or ingested course materials +- to a draft pack +- to an actively curated review session +- to a promoted reviewed pack + +## Why this matters + +A major design goal of Didactopus is lowering the **activation-energy hump**. +Even when good online course content exists, a learner or curator may still stall because: + +- materials are scattered +- multiple draft packs accumulate +- there is no single place to organize review work +- switching between projects is awkward + +The workspace manager addresses that by making Didactopus feel more like a practical +local tool and less like a pile of disconnected artifacts. diff --git a/.update_readmes/20260314_131846__090-didactopus-draft-pack-import-workflow-update__README.md b/.update_readmes/20260314_131846__090-didactopus-draft-pack-import-workflow-update__README.md new file mode 100644 index 0000000..0ca92c5 --- /dev/null +++ b/.update_readmes/20260314_131846__090-didactopus-draft-pack-import-workflow-update__README.md @@ -0,0 +1,48 @@ +# Didactopus + +Didactopus is a local-first AI-assisted autodidactic mastery platform built around +concept graphs, evaluator-driven evidence, adaptive planning, mastery ledgers, +curriculum ingestion, and human review of generated draft packs. + +## This revision + +This revision adds a **draft-pack import workflow** on top of the workspace manager. + +The goal is to let a user take a newly generated draft pack from the ingestion +pipeline and bring it into a managed review workspace in one step. + +## Why this matters + +A major source of friction in turning online course contents into usable study +domains is not only extraction difficulty, but also the messy handoff between: + +- generated draft artifacts +- review workspaces +- ongoing curation +- promoted reviewed packs + +That handoff can easily become another activation-energy barrier. + +This import workflow reduces that barrier by making it straightforward to: + +1. choose a draft pack directory +2. create or target a workspace +3. copy/import the draft pack into that workspace +4. begin review immediately in the UI + +## What is included + +- workspace import operation +- local API endpoint for importing a draft pack into a workspace +- React UI controls for import +- preservation of imported draft-pack files +- sample import source directory +- sample workspace with imported draft pack + +## Core workflow + +1. generate a draft pack via ingestion +2. create a workspace or choose an existing one +3. import the draft pack into that workspace +4. open the workspace in the review UI +5. curate and promote it diff --git a/.update_readmes/20260314_131849__095-didactopus-import-validation-safety-update__README.md b/.update_readmes/20260314_131849__095-didactopus-import-validation-safety-update__README.md new file mode 100644 index 0000000..99e2f20 --- /dev/null +++ b/.update_readmes/20260314_131849__095-didactopus-import-validation-safety-update__README.md @@ -0,0 +1,48 @@ +# Didactopus + +Didactopus is a local-first AI-assisted autodidactic mastery platform built around +concept graphs, evaluator-driven evidence, adaptive planning, mastery ledgers, +curriculum ingestion, and human review of generated draft packs. + +## This revision + +This revision adds an **import validation and safety layer** to the draft-pack +import workflow. + +The goal is to make importing generated packs into review workspaces safer, +clearer, and easier to trust. + +## Why this matters + +If the draft-pack import step is risky or opaque, it becomes another point where +a user may hesitate or stall. That would undercut the broader goal of helping +users get over the activation-energy hump of turning online course contents into +usable Didactopus learning domains. + +This layer reduces that risk by adding: + +- required-file validation +- schema/version summary inspection +- overwrite warnings +- import preview endpoint +- import error reporting +- basic pack-health reporting before copy/import + +## What is included + +- draft-pack validator +- import preview model +- overwrite-safety checks +- preview and import API endpoints +- updated React UI for preview-before-import +- sample valid and invalid draft packs +- tests for validation and safety behavior + +## Core workflow + +1. point the UI at a source draft-pack directory +2. preview validation results +3. review warnings or blocking errors +4. choose whether overwrite is allowed +5. import into workspace +6. continue directly into review diff --git a/.update_readmes/20260314_131853__100-didactopus-full-pack-validation-update__README.md b/.update_readmes/20260314_131853__100-didactopus-full-pack-validation-update__README.md new file mode 100644 index 0000000..f1f5fe2 --- /dev/null +++ b/.update_readmes/20260314_131853__100-didactopus-full-pack-validation-update__README.md @@ -0,0 +1,62 @@ +# Didactopus + +Didactopus is a local-first AI-assisted autodidactic mastery platform built around +concept graphs, evaluator-driven evidence, adaptive planning, mastery ledgers, +curriculum ingestion, and human review of generated draft packs. + +## This revision + +This revision adds a **full pack-validation layer** that checks cross-file coherence +for Didactopus draft packs before import and during review. + +The goal is to move beyond “does the directory exist and parse?” toward a more +Didactopus-native notion of whether a pack is structurally coherent enough to use. + +## Why this matters + +A generated pack may look fine at first glance and still contain internal problems: + +- roadmap stages referencing missing concepts +- projects depending on nonexistent concepts +- duplicate concept ids +- rubrics with malformed structure +- empty or weak metadata +- inconsistent pack identity information + +Those issues can become another activation-energy barrier. A user who has already +done the hard work of finding course materials and generating a draft pack should +not have to manually discover every structural issue one file at a time. + +## What is included + +- full pack validator +- cross-file validation across: + - `pack.yaml` + - `concepts.yaml` + - `roadmap.yaml` + - `projects.yaml` + - `rubrics.yaml` +- validation summary model +- import preview now includes pack-validation findings +- review UI panels for validation errors and warnings +- sample valid and invalid packs +- tests for coherence checks + +## Core checks + +Current scaffold validates: + +- required files exist +- YAML parsing for all key files +- pack metadata presence +- duplicate concept ids +- roadmap concepts exist in `concepts.yaml` +- project prerequisites exist in `concepts.yaml` +- rubric structure presence +- empty or suspiciously weak concept entries + +## Design stance + +This is a structural coherence layer, not a guarantee of pedagogical quality. +It makes the import path safer and clearer, while still leaving room for later +semantic and domain-specific validation. diff --git a/.update_readmes/20260314_131856__105-didactopus-coverage-alignment-update__README.md b/.update_readmes/20260314_131856__105-didactopus-coverage-alignment-update__README.md new file mode 100644 index 0000000..eaf10a6 --- /dev/null +++ b/.update_readmes/20260314_131856__105-didactopus-coverage-alignment-update__README.md @@ -0,0 +1,15 @@ +# Didactopus + +This update adds a **coverage-and-alignment QA layer**. + +It checks whether concepts, mastery signals, checkpoints, projects, and rubrics +actually line up well enough to support a credible mastery path. + +Current checks: +- concepts absent from roadmap stages +- concepts absent from checkpoint language +- concepts absent from project prerequisites +- concepts never covered by either checkpoints or projects +- mastery signals not reflected in checkpoints or deliverables +- rubric criteria with weak overlap to mastery/project language +- projects that cover too little of the concept set diff --git a/.update_readmes/20260314_131859__110-didactopus-curriculum-path-quality-update__README.md b/.update_readmes/20260314_131859__110-didactopus-curriculum-path-quality-update__README.md new file mode 100644 index 0000000..d912eab --- /dev/null +++ b/.update_readmes/20260314_131859__110-didactopus-curriculum-path-quality-update__README.md @@ -0,0 +1,31 @@ +# Didactopus + +Didactopus is a local-first AI-assisted autodidactic mastery platform built around +concept graphs, evaluator-driven evidence, adaptive planning, mastery ledgers, +curriculum ingestion, and human review of generated draft packs. + +## This revision + +This revision adds a **curriculum path quality layer**. + +The goal is to analyze whether a pack's roadmap looks like a sensible learner +progression rather than merely a list of stages. + +## What is included + +- curriculum path quality analysis module +- heuristic checks for stage progression quality +- path-quality findings included in import preview +- UI display for curriculum path warnings +- sample packs and tests + +## Current path-quality checks + +This scaffold includes checks for: +- empty stages +- stages with no checkpoint activity +- concepts never referenced in checkpoints or projects +- capstones/projects placed very early +- dead-end late stages with no assessment density +- suspicious stage-size imbalance +- abrupt prerequisite-load jumps across stages diff --git a/.update_readmes/20260314_131902__115-didactopus-evaluator-alignment-update__README.md b/.update_readmes/20260314_131902__115-didactopus-evaluator-alignment-update__README.md new file mode 100644 index 0000000..ded5f88 --- /dev/null +++ b/.update_readmes/20260314_131902__115-didactopus-evaluator-alignment-update__README.md @@ -0,0 +1,3 @@ +# Didactopus + +This update adds an evaluator-to-pack alignment QA layer. diff --git a/.update_readmes/20260314_131906__120-didactopus-evidence-flow-mastery-ledger-update__README.md b/.update_readmes/20260314_131906__120-didactopus-evidence-flow-mastery-ledger-update__README.md new file mode 100644 index 0000000..73c9ef4 --- /dev/null +++ b/.update_readmes/20260314_131906__120-didactopus-evidence-flow-mastery-ledger-update__README.md @@ -0,0 +1,6 @@ +# Didactopus + +This update adds an **evidence-flow and mastery-ledger QA layer**. + +It checks whether evaluator outputs, evidence types, and assessment artifacts can +be translated into learner mastery-ledger records in a coherent way. diff --git a/.update_readmes/20260314_131909__125-didactopus-graph-prereq-analysis-update__README.md b/.update_readmes/20260314_131909__125-didactopus-graph-prereq-analysis-update__README.md new file mode 100644 index 0000000..6dac977 --- /dev/null +++ b/.update_readmes/20260314_131909__125-didactopus-graph-prereq-analysis-update__README.md @@ -0,0 +1,38 @@ +# Didactopus + +Didactopus is a local-first AI-assisted autodidactic mastery platform built around +concept graphs, evaluator-driven evidence, adaptive planning, mastery ledgers, +curriculum ingestion, and human review of generated draft packs. + +## This revision + +This revision adds a **graph-aware prerequisite analysis layer**. + +The goal is to inspect a pack not just as a set of files or even as a semantically +plausible curriculum draft, but as an actual dependency graph whose structure may +reveal deeper curation problems. + +## Why this matters + +A pack can be syntactically valid, cross-file coherent, and even semantically plausible, +yet still have a concept graph that is hard to learn from or maintain. Typical examples: + +- prerequisite cycles +- isolated concepts with no curricular integration +- bottleneck concepts with too many downstream dependencies +- suspiciously flat domains with almost no dependency structure +- suspiciously deep chains suggesting over-fragmentation + +Those graph problems can still raise the activation-energy cost of using a pack, +because they make learning paths harder to trust and revise. + +## What is included + +- prerequisite graph analysis module +- cycle detection +- isolated concept detection +- bottleneck concept detection +- flatness and chain-depth heuristics +- graph findings included in import preview +- UI panel for graph-analysis warnings +- sample packs and tests diff --git a/.update_readmes/20260314_131913__130-didactopus-semantic-qa-update__README.md b/.update_readmes/20260314_131913__130-didactopus-semantic-qa-update__README.md new file mode 100644 index 0000000..cc6948a --- /dev/null +++ b/.update_readmes/20260314_131913__130-didactopus-semantic-qa-update__README.md @@ -0,0 +1,52 @@ +# Didactopus + +Didactopus is a local-first AI-assisted autodidactic mastery platform built around +concept graphs, evaluator-driven evidence, adaptive planning, mastery ledgers, +curriculum ingestion, and human review of generated draft packs. + +## This revision + +This revision adds a **domain-pack semantic QA layer**. + +The goal is to go beyond file integrity and cross-file coherence, and start asking +whether a generated Didactopus pack looks semantically plausible as a learning domain. + +## Why this matters + +A pack may pass structural validation and still have higher-level weaknesses such as: + +- near-duplicate concepts with different wording +- prerequisites that look suspiciously thin or over-compressed +- missing bridge concepts between stages +- concepts that are probably too broad and should be split +- concepts with names that imply overlap or ambiguity + +Those problems can still slow a learner or curator down, which means they still +contribute to the activation-energy hump Didactopus is meant to reduce. + +## What is included + +- semantic QA analysis module +- heuristic semantic checks +- semantic QA findings included in import preview +- UI panel for semantic QA warnings +- sample packs showing semantic QA output +- tests for semantic QA behavior + +## Current semantic QA checks + +This scaffold includes heuristic checks for: + +- near-duplicate concept titles +- over-broad concept titles +- suspiciously thin prerequisite chains +- missing bridge concepts between roadmap stages +- concepts with very similar descriptions +- singleton advanced stages with no visible bridge support + +## Design stance + +This is still a heuristic layer, not a final semantic truth engine. + +Its purpose is to surface likely curation issues early enough that a reviewer can +correct them before those issues turn into confusion or wasted effort. diff --git a/.update_readmes/20260314_131917__135-didactopus-admin-curation-layer__README.md b/.update_readmes/20260314_131917__135-didactopus-admin-curation-layer__README.md new file mode 100644 index 0000000..3e9736a --- /dev/null +++ b/.update_readmes/20260314_131917__135-didactopus-admin-curation-layer__README.md @@ -0,0 +1,12 @@ +# Didactopus Admin Curation Layer + +This update extends the previous admin/learner workflow scaffold with a deeper +admin and curation layer. + +## Added in this scaffold + +- pack validation review surfaces in the admin UI +- attribution / provenance inspection surfaces in the admin UI +- evaluator trace inspection surfaces +- richer pack authoring forms instead of raw JSON-only editing +- backend endpoints for validation summaries, provenance inspection, and evaluator traces diff --git a/.update_readmes/20260314_131920__140-didactopus-admin-learner-ui-workflows__README.md b/.update_readmes/20260314_131920__140-didactopus-admin-learner-ui-workflows__README.md new file mode 100644 index 0000000..ccf6722 --- /dev/null +++ b/.update_readmes/20260314_131920__140-didactopus-admin-learner-ui-workflows__README.md @@ -0,0 +1,37 @@ +# Didactopus Admin + Learner UI Workflows + +This update builds the next layer on top of the productionization scaffold by wiring +the frontend toward real workflow surfaces: + +- login with token storage +- token refresh handling +- learner dashboard flow +- evaluator-history view +- learner-management view +- admin pack creation / publication view + +## Included + +### Frontend +- login screen +- auth context with token refresh scaffold +- learner dashboard +- evaluator history panel +- learner management panel +- admin pack editor / publisher panel +- shared API client + +### Backend additions +- learner listing endpoint +- admin pack listing endpoint +- admin pack publication toggle endpoint + +## Scope +This remains a scaffold intended to connect the architectural pieces and establish +usable interaction flows. It is not yet a polished production UI. + +## Intended next step +- integrate richer form validation +- add pack schema editing tools +- connect evaluator traces and rubric results +- add paginated audit history diff --git a/.update_readmes/20260314_131923__145-didactopus-agent-audit-and-key-rotation-layer__README.md b/.update_readmes/20260314_131923__145-didactopus-agent-audit-and-key-rotation-layer__README.md new file mode 100644 index 0000000..937f2ae --- /dev/null +++ b/.update_readmes/20260314_131923__145-didactopus-agent-audit-and-key-rotation-layer__README.md @@ -0,0 +1,27 @@ +# Didactopus Agent Audit Logging + Key Rotation Layer + +This update extends the service-account scaffold with two operational controls: + +- **audit logging** for machine-initiated activity +- **key rotation / revocation scaffolding** for service accounts + +## Added in this scaffold + +- audit log records for service-account actions +- request-level audit helper for agent operations +- service-account secret rotation endpoint +- service-account enable/disable endpoint +- admin UI for viewing audit events and rotating credentials + +## Why this matters + +A serious AI learner deployment needs more than scoped credentials. + +It also needs to answer: + +- which service account did what? +- when did it do it? +- what endpoint or workflow did it invoke? +- can we replace or revoke a compromised credential? + +This layer makes service-account usage more accountable and more maintainable. diff --git a/.update_readmes/20260314_131926__150-didactopus-agent-service-account-layer__README.md b/.update_readmes/20260314_131926__150-didactopus-agent-service-account-layer__README.md new file mode 100644 index 0000000..d3b1396 --- /dev/null +++ b/.update_readmes/20260314_131926__150-didactopus-agent-service-account-layer__README.md @@ -0,0 +1,43 @@ +# Didactopus Agent Service Account Layer + +This update extends the deployment-policy and agent-hooks scaffold with a +**first-class service-account model for AI learners and other non-human agents**. + +## Added in this scaffold + +- service-account records +- scoped API tokens for agents +- capability scopes for learner workflows +- direct agent authentication endpoint +- scope checks for agent operations +- admin UI for viewing service accounts and their scopes + +## Why this matters + +An AI learner should not need to masquerade as a human user session. + +With this layer, an installation can: +- create a dedicated machine identity +- give it only the scopes it needs +- allow it to operate through the same API surfaces as the UI +- keep agent permissions narrower than full admin access when appropriate + +## Example scopes + +- `packs:read` +- `packs:write_personal` +- `contributions:submit` +- `learners:read` +- `learners:write` +- `recommendations:read` +- `evaluators:submit` +- `evaluators:read` +- `governance:read` +- `governance:write` + +## Strong next step + +- key rotation and revocation UI +- service-account ownership and audit trails +- structured workflow schema export for agents +- explicit agent-run logs tied to service-account identity diff --git a/.update_readmes/20260314_131930__155-didactopus-animated-concept-graph-layer__README.md b/.update_readmes/20260314_131930__155-didactopus-animated-concept-graph-layer__README.md new file mode 100644 index 0000000..303e447 --- /dev/null +++ b/.update_readmes/20260314_131930__155-didactopus-animated-concept-graph-layer__README.md @@ -0,0 +1,41 @@ +# Didactopus Animated Concept Graph Layer + +This update extends the learning-animation scaffold with an **animated concept-graph view**. + +## What it adds + +- concept-graph playback frames +- node state transitions over time +- prerequisite edge rendering data +- API endpoint for graph animation payloads +- UI prototype for animated concept graph playback + +## Why this matters + +A bar-chart timeline is useful, but a concept graph better matches how Didactopus +represents mastery structure: + +- concepts as nodes +- prerequisites as directed edges +- mastery progression as node color/size change +- availability/unlock state as a visible transition + +This makes learning progression easier to interpret for: +- human learners +- AI-learner debugging +- curriculum designers +- reviewers comparing different runs + +## Animation model + +Each frame includes: +- node scores +- node status (`locked`, `available`, `active`, `mastered`) +- simple node size hints derived from score +- static prerequisite edges + +Later versions could add: +- force-directed layouts +- semantic cross-pack links +- edge highlighting when prerequisite satisfaction changes +- side-by-side learner comparison diff --git a/.update_readmes/20260314_131933__160-didactopus-artifact-registry-layer__README.md b/.update_readmes/20260314_131933__160-didactopus-artifact-registry-layer__README.md new file mode 100644 index 0000000..9ced153 --- /dev/null +++ b/.update_readmes/20260314_131933__160-didactopus-artifact-registry-layer__README.md @@ -0,0 +1,24 @@ +# Didactopus Worker-Backed Artifact Registry Layer + +This update extends the media-rendering pipeline with a **worker-backed artifact registry**. + +## What it adds + +- artifact registry records +- render job records +- worker-oriented job lifecycle states +- artifact listing and lookup endpoints +- bundle registration into a persistent catalog +- UI prototype for browsing render jobs and produced artifacts + +## Why this matters + +The previous layer could create render bundles, but the outputs were still basically +filesystem-level side effects. This layer promotes artifacts into first-class Didactopus +objects so the system can: + +- track render requests over time +- associate artifacts with learners and packs +- record job status (`queued`, `running`, `completed`, `failed`) +- expose artifacts in the UI and API +- support future download, retention, and publication policies diff --git a/.update_readmes/20260314_132003__165-didactopus-attribution-provenance-update__README.md b/.update_readmes/20260314_132003__165-didactopus-attribution-provenance-update__README.md new file mode 100644 index 0000000..5f35787 --- /dev/null +++ b/.update_readmes/20260314_132003__165-didactopus-attribution-provenance-update__README.md @@ -0,0 +1,37 @@ +# Didactopus + +This update adds an **attribution, provenance, and license-compliance scaffold** for domain packs. + +It is designed for open-courseware ingestion workflows, including sources such as MIT OpenCourseWare, +where downstream reuse may be allowed but requires preserving provenance and meeting license terms. + +## Why this matters + +A Didactopus domain pack should not be a black box. If source materials contributed to the pack, +the pack should carry machine-readable and human-readable provenance so that: +- attribution can be generated automatically +- remix/adaptation status can be recorded +- excluded third-party content can be flagged +- downstream redistribution can be audited more safely +- human learners and maintainers can inspect where content came from + +## Included in this update + +- source provenance models +- attribution bundle generator +- attribution QA checks +- sample `sources.yaml` +- sample `ATTRIBUTION.md` +- pack-level provenance manifest +- MIT OCW-oriented notes for compliance-aware ingestion + +## Pack artifacts introduced here + +- `sources.yaml` — source inventory and licensing metadata +- `ATTRIBUTION.md` — human-readable attribution report +- `provenance_manifest.json` — machine-readable normalized provenance output + +## Important note + +This scaffold helps operationalize attribution and provenance handling. +It is **not** legal advice. diff --git a/.update_readmes/20260314_132010__170-didactopus-auth-db-async-evaluator-prototype__README.md b/.update_readmes/20260314_132010__170-didactopus-auth-db-async-evaluator-prototype__README.md new file mode 100644 index 0000000..49f733c --- /dev/null +++ b/.update_readmes/20260314_132010__170-didactopus-auth-db-async-evaluator-prototype__README.md @@ -0,0 +1,57 @@ +# Didactopus Auth + DB + Async Evaluator Prototype + +This update extends the backend API prototype with scaffolding for: + +- authentication and multi-user separation +- a real database backend (SQLite via SQLAlchemy) +- evaluator job submission +- asynchronous result ingestion into learner mastery records + +## What is included + +### Authentication and user separation +This prototype introduces: +- user records +- simple token-based auth scaffold +- learner-state ownership checks +- per-user learner records + +This is intentionally minimal and suitable for local development, not production hardening. + +### Database backend +The file-backed JSON store is replaced here with a relational persistence scaffold: +- SQLite database by default +- SQLAlchemy ORM models +- tables for users, packs, learners, mastery records, evidence events, evaluator jobs + +### Async evaluator jobs +This prototype adds: +- evaluator job submission endpoint +- background worker scaffold +- evaluator results persisted to the database +- resulting evidence events applied into learner mastery records + +## Why this matters + +This is the first version that structurally supports: +- multiple users +- persistent learner history in a real database +- evaluator-driven evidence arriving later than the UI request that triggered it + +That is the correct shape for turning Didactopus into a genuine multi-user learning platform. + +## Important note + +This remains a prototype scaffold: +- auth is deliberately simple +- SQLite is used for ease of inspection +- background job execution uses FastAPI background tasks rather than a production queue +- secrets, password hardening, and deployment concerns still need a later pass + +## Next likely step + +- replace simple token auth with stronger session/JWT handling +- migrate from SQLite to PostgreSQL +- add role-based authorization +- move evaluator jobs to a real queue such as Celery/RQ/Arq +- expose evaluator traces and job history in the learner UI diff --git a/.update_readmes/20260314_132013__175-didactopus-backend-api-prototype__README.md b/.update_readmes/20260314_132013__175-didactopus-backend-api-prototype__README.md new file mode 100644 index 0000000..953fc82 --- /dev/null +++ b/.update_readmes/20260314_132013__175-didactopus-backend-api-prototype__README.md @@ -0,0 +1,46 @@ +# Didactopus Backend API Prototype + +This update adds a small real backend API scaffold for: + +- pack registry listing +- learner-state persistence outside the browser +- evaluator-result ingestion into mastery records + +## What is included + +### Backend +A lightweight FastAPI-style scaffold with: +- `GET /api/packs` +- `GET /api/packs/{pack_id}` +- `GET /api/learners/{learner_id}/state` +- `PUT /api/learners/{learner_id}/state` +- `POST /api/learners/{learner_id}/evidence` +- `GET /api/learners/{learner_id}/recommendations/{pack_id}` + +The backend uses simple file-backed JSON storage so the prototype remains easy to inspect and modify. + +### Frontend +The learner UI is updated to: +- load pack registry from the backend +- load learner state from the backend +- persist learner state through the backend +- submit simulated evidence events to the backend +- render recommendations returned by the backend + +## Why this matters + +This is the first step from a single-browser prototype toward a genuinely multi-session, multi-user system: +- learner state no longer has to live only in local storage +- recommendations can be centralized +- evaluator output can enter the same evidence pathway as UI-generated events +- future real evaluators can update learner state without changing the learner UI architecture + +## Prototype scope + +This remains intentionally small: +- file-backed storage +- no authentication yet +- no database dependency yet +- simulated evaluator/evidence flow still simple + +That makes it appropriate for rapid iteration while preserving a clean path to later migration. diff --git a/.update_readmes/20260314_132017__180-didactopus-contribution-management-layer__README.md b/.update_readmes/20260314_132017__180-didactopus-contribution-management-layer__README.md new file mode 100644 index 0000000..1885eda --- /dev/null +++ b/.update_readmes/20260314_132017__180-didactopus-contribution-management-layer__README.md @@ -0,0 +1,35 @@ +# Didactopus Contribution Management Layer + +This update extends the review-governance scaffold with a **contribution management layer**. + +## Added in this scaffold + +- contributor submission records +- pack diffs between versions +- approval gates tied to validation/provenance status +- reviewer task queue / notification scaffold +- admin UI views for submissions, diffs, and review tasks + +## Why this matters + +Once Didactopus supports outside contributors, maintainers need a structured way to: +- receive submissions +- compare proposed revisions against current versions +- see whether QA/provenance gates are satisfied +- assign or at least surface review work +- keep an audit trail of what was submitted and why it was accepted or rejected + +## Scope + +This remains a scaffold: +- diffs are summary-oriented rather than line-perfect +- notification/task queues are prototype records +- gate checks are simple but explicit +- contributor identity is tied to existing users rather than a separate contributor model + +## Strong next step + +- richer semantic diffs for concepts/onboarding/compliance +- required reviewer assignment rules +- notifications via email/chat connectors +- policy engine for gating approvals diff --git a/.update_readmes/20260314_132021__185-didactopus-course-compliance-ui-prototype__README.md b/.update_readmes/20260314_132021__185-didactopus-course-compliance-ui-prototype__README.md new file mode 100644 index 0000000..6c2739c --- /dev/null +++ b/.update_readmes/20260314_132021__185-didactopus-course-compliance-ui-prototype__README.md @@ -0,0 +1,48 @@ +# Didactopus + +This package adds two substantial prototype layers: + +1. a **course-ingestion compliance layer** +2. a **real learner-facing UI prototype** + +## What this update covers + +### Course-ingestion compliance +The compliance layer is designed for domain packs created from open courseware and other external instructional sources. + +It includes: +- source inventory handling +- attribution and provenance records +- pack-level license flags +- compliance QA checks +- exclusion tracking for third-party content +- redistribution-risk signaling + +### Learner-facing UI prototype +The prototype UI is designed to be usable by humans approaching a new topic. + +It implements: +- topic/domain selection +- first-session onboarding +- “what should I do next?” cards +- visible mastery-map progress +- milestone/reward feedback +- transparent “why the system recommends this” explanations + +## UX stance + +Didactopus should help a novice get moving quickly, not present a second subject to learn first. + +The first session should: +- make the next action obvious +- give quick feedback +- show visible progress +- feel encouraging rather than bureaucratic + +## Prototype scope + +This is still a prototype scaffold, but the UI and compliance pieces are concrete enough to: +- test interaction patterns +- validate data shapes +- demonstrate provenance-aware ingestion +- serve as a starting point for a fuller implementation diff --git a/.update_readmes/20260314_132024__190-didactopus-deployment-policy-and-agent-hooks__README.md b/.update_readmes/20260314_132024__190-didactopus-deployment-policy-and-agent-hooks__README.md new file mode 100644 index 0000000..eae3ae0 --- /dev/null +++ b/.update_readmes/20260314_132024__190-didactopus-deployment-policy-and-agent-hooks__README.md @@ -0,0 +1,46 @@ +# Didactopus Deployment Policy + Agent Hooks Layer + +This update extends the dual-lane policy scaffold with two related concerns: + +1. **Deployment policy settings** + - single-user / private-first + - team / lab + - community repository + +2. **AI learner / agent hook parity** + - explicit API surfaces for agentic learners + - capability discovery endpoints + - task-oriented endpoints parallel to the UI workflows + - access to pack, learner, evaluator, and recommendation workflows without relying on the UI + +## Why this matters + +Didactopus should remain usable in two modes: + +- a human using the UI directly +- an AI learner or agentic orchestrator using the API directly + +The AI learner should not lose capability simply because a human-facing UI exists. +Instead, the UI should be understood as a thin client over API functionality. + +## What is added + +- deployment policy profile model and endpoint +- policy-aware defaults for pack lane behavior +- agent capability manifest endpoint +- agent learner workflow endpoints +- explicit notes documenting API parity with UI workflows + +## AI learner capability check + +This scaffold makes the AI-learner situation clearer: + +- yes, the API still exposes the essential learner operations +- yes, pack access, recommendations, evaluator job submission, and learner-state access remain directly callable +- yes, there is now an explicit capability-discovery endpoint so an agent can inspect what the installation supports + +## Strong next step + +- add service-account / non-human agent credentials +- formalize machine-usable schemas for workflows and actions +- add structured action planning endpoint for agentic learners diff --git a/.update_readmes/20260314_132027__195-didactopus-dual-lane-policy-layer__README.md b/.update_readmes/20260314_132027__195-didactopus-dual-lane-policy-layer__README.md new file mode 100644 index 0000000..df1fefa --- /dev/null +++ b/.update_readmes/20260314_132027__195-didactopus-dual-lane-policy-layer__README.md @@ -0,0 +1,47 @@ +# Didactopus Dual-Lane Policy Layer + +This update extends the contribution-management scaffold with a **dual-lane policy model**: + +- a **personal lane** for individuals building domain packs for their own use +- a **community lane** for contributed packs that enter shared review and publication workflows + +## Design intent + +A single user working privately with Didactopus should **not** be blocked by governance overhead +when constructing packs for their own purposes. + +At the same time, community-shared packs should still be subject to: +- contribution intake +- validation and provenance gates +- reviewer workflows +- approval before publication + +## Added in this scaffold + +- pack policy lane metadata (`personal`, `community`) +- bypass rules for personal packs +- community-only gate enforcement for publication workflows +- UI distinction between personal-authoring and community-submission flows +- reviewer-assignment and approval-policy scaffolding for community packs only + +## Resulting behavior + +### Personal lane +A user can: +- create and revise packs directly +- publish locally for their own use +- bypass reviewer task queues +- inspect validation/provenance without being blocked by them + +### Community lane +A contributor can: +- submit a pack or revision for review +- see gate summaries and diffs +- enter reviewer assignment and approval workflow +- require policy satisfaction before publish + +## Strong next step + +- per-installation policy settings +- optional stricter local policies for teams or labs +- semantic diffing and structured reviewer checklists diff --git a/.update_readmes/20260314_132030__200-didactopus-layout-aware-graph-engine-layer__README.md b/.update_readmes/20260314_132030__200-didactopus-layout-aware-graph-engine-layer__README.md new file mode 100644 index 0000000..5054235 --- /dev/null +++ b/.update_readmes/20260314_132030__200-didactopus-layout-aware-graph-engine-layer__README.md @@ -0,0 +1,45 @@ +# Didactopus Layout-Aware Graph Engine Layer + +This update extends the animated concept-graph scaffold with a **layout-aware graph engine**. + +## What it adds + +- stable node positioning +- pack-authored coordinates +- automatic layered layout fallback +- cross-pack concept links +- SVG frame export scaffolding +- UI prototype with stable animated graph playback + +## Why this matters + +Animated concept graphs are much more readable when node positions do not jump around. +This layer makes the graph a more faithful representation of a mastery ecosystem by adding: + +- deterministic coordinates +- prerequisite layering +- optional author-specified placement +- cross-pack links for broader learning pathways +- export-ready frame generation for later GIF/MP4 pipelines + +## Layout model + +The engine uses this priority order: + +1. explicit pack-authored coordinates +2. automatic layered layout from prerequisite depth +3. deterministic horizontal spacing inside each depth layer + +## Export model + +This scaffold includes: +- graph frame payloads from the API +- SVG frame export helper script +- one SVG per frame for later conversion to GIF/MP4 with external tools + +## Strong next step + +- force-directed refinement +- edge highlighting on unlock transitions +- cross-pack supergraph views +- direct GIF/MP4 rendering pipeline diff --git a/.update_readmes/20260314_132033__205-didactopus-learner-state-progression-update__README.md b/.update_readmes/20260314_132033__205-didactopus-learner-state-progression-update__README.md new file mode 100644 index 0000000..602cb8e --- /dev/null +++ b/.update_readmes/20260314_132033__205-didactopus-learner-state-progression-update__README.md @@ -0,0 +1,17 @@ +# Didactopus + +This update adds a **learner-state progression engine scaffold**. + +It models how mastery records can evolve over time from repeated evidence, with: +- score aggregation +- confidence reinforcement and decay +- prerequisite-gated advancement +- next-step recommendations + +Current components: +- learner state model +- evidence application engine +- confidence update logic +- prerequisite-gated readiness checks +- recommendation engine +- tests and sample data diff --git a/.update_readmes/20260314_132036__210-didactopus-learning-animation-layer__README.md b/.update_readmes/20260314_132036__210-didactopus-learning-animation-layer__README.md new file mode 100644 index 0000000..5d86563 --- /dev/null +++ b/.update_readmes/20260314_132036__210-didactopus-learning-animation-layer__README.md @@ -0,0 +1,46 @@ +# Didactopus Run/Session Correlation + Learning Animation Layer + +This update extends the agent audit / key rotation scaffold with: + +- **run/session correlation** for learner episodes +- **workflow logs** tied to learner runs +- **animation data endpoints** for replaying learning progress +- a **UI prototype** that can animate a learner's mastery changes over time + +## Why this matters + +A single audit event is useful, but it does not tell the full story of a learning episode. + +For both human learners and AI learners, Didactopus should be able to represent: + +- when a learning run began +- what sequence of actions happened +- how mastery estimates changed during the run +- how recommendations shifted as competence improved + +That makes it possible to: +- inspect learner trajectories +- debug agentic learning behavior +- demonstrate the learning process to users, reviewers, or researchers +- create visualizations and animations of learning over time + +## Added in this scaffold + +- learner run/session records +- workflow event log records +- animation frame generation from learner history +- API endpoints for run creation, workflow-event logging, and animation playback data +- UI prototype for replaying learning progression as an animation + +## Animation concept + +This scaffold uses a simple time-series animation model: +- each frame corresponds to a learner-history event +- each concept's mastery score is shown per frame +- the UI can replay those frames with a timer + +Later implementations could support: +- graph/network animation +- concept unlock transitions +- recommendation timeline overlays +- side-by-side human vs AI learner comparison diff --git a/.update_readmes/20260314_132039__215-didactopus-live-learner-ui-prototype__README.md b/.update_readmes/20260314_132039__215-didactopus-live-learner-ui-prototype__README.md new file mode 100644 index 0000000..e67b5ba --- /dev/null +++ b/.update_readmes/20260314_132039__215-didactopus-live-learner-ui-prototype__README.md @@ -0,0 +1,54 @@ +# Didactopus Live Learner UI Prototype + +This update connects the learner-facing UI prototype to a live in-browser learner-state +and orchestration loop. + +## What this prototype does + +It now drives the interface from live state rather than static cards: +- topic/domain selection +- first-session onboarding +- recommendation generation from learner state +- visible mastery-map progress from mastery records +- milestone / reward feedback +- transparent "why this is recommended" explanations +- simulated evidence application that updates learner mastery live +- source attribution / compliance panel for provenance-sensitive packs + +## Architecture + +### Frontend +A React/Vite single-page prototype that manages: +- learner profile selection +- domain pack selection +- learner mastery records +- recommendation cards +- mastery map rendering +- milestone log +- attribution/compliance display + +### State engine +A lightweight JS orchestration layer mirrors the Didactopus Python scaffolds: +- evidence application +- score aggregation +- confidence updates +- prerequisite-gated unlocking +- next-step recommendation generation +- reinforcement targeting +- simple claim-readiness estimation + +## Why this matters + +This is closer to a human-usable experience: +- the learner can see the effect of actions immediately +- the "why next?" logic is inspectable +- progress feels visible and rewarding +- the system remains simple enough for a novice to approach + +## Next likely step + +Wire this prototype to a real backend so that: +- domain packs are loaded from pack files +- learner state persists across sessions +- evaluator results update mastery records automatically +- attribution/compliance artifacts are derived from actual ingested sources diff --git a/.update_readmes/20260314_132042__220-didactopus-media-rendering-pipeline-layer__README.md b/.update_readmes/20260314_132042__220-didactopus-media-rendering-pipeline-layer__README.md new file mode 100644 index 0000000..2feef88 --- /dev/null +++ b/.update_readmes/20260314_132042__220-didactopus-media-rendering-pipeline-layer__README.md @@ -0,0 +1,46 @@ +# Didactopus Direct Media Rendering Pipeline Layer + +This update extends the layout-aware graph engine with a **direct media-rendering pipeline** +for turning learning animations into shareable artifacts. + +## What it adds + +- SVG frame export integration +- GIF manifest generation +- MP4 manifest generation +- FFmpeg-oriented render script scaffolding +- API endpoint for media render jobs +- UI prototype for creating export bundles + +## Why this matters + +Didactopus should not stop at interactive playback. It should also be able to produce +portable visual artifacts for: + +- research presentations +- learner progress sharing +- curriculum review +- AI learner debugging +- repository documentation + +This layer provides a structured path from graph animation payloads to: +- frame directories +- render manifests +- GIF/MP4-ready job bundles + +## Scope + +This scaffold produces: +- exported SVG frames +- JSON render manifests +- shell script scaffolding for FFmpeg conversion + +It does **not** embed FFmpeg execution into the API server itself. +That is a deliberate separation so rendering can be delegated to a worker or offline job. + +## Strong next step + +- actual worker-backed render execution +- render status tracking +- downloadable media artifact registry +- parameterized themes, sizes, and captions diff --git a/.update_readmes/20260314_132048__225-didactopus-orchestration-ux-update__README.md b/.update_readmes/20260314_132048__225-didactopus-orchestration-ux-update__README.md new file mode 100644 index 0000000..622fd14 --- /dev/null +++ b/.update_readmes/20260314_132048__225-didactopus-orchestration-ux-update__README.md @@ -0,0 +1,63 @@ +# Didactopus + +This update adds a **learner-run orchestration layer scaffold** with explicit **UX design guidance**. + +The goal is to tie together: +- domain-pack selection +- learner onboarding +- recommendation generation +- evaluator invocation +- mastery-ledger updates +- stopping criteria for usable expertise +- humane, low-friction user experience + +## UX stance + +Didactopus should not require the learner to first master Didactopus. + +A person approaching a new topic should be able to: +- choose a topic +- understand what to do next +- get feedback quickly +- see progress clearly +- recover easily from mistakes or uncertainty +- experience the process as rewarding rather than bureaucratic + +## UX principles + +### 1. Low activation energy +The first session should produce visible progress quickly. + +### 2. Clear next action +At every point, the learner should know what to do next. + +### 3. Gentle structure +The system should scaffold without becoming oppressive or confusing. + +### 4. Reward loops +Progress should feel visible and meaningful: +- concept unlocks +- streaks or milestones +- mastery-map filling +- capstone readiness indicators +- “you can now do X” style feedback + +### 5. Human-readable state +The learner should be able to inspect: +- what the system thinks they know +- why it thinks that +- what evidence changed the estimate +- what is blocking advancement + +### 6. Graceful fallback +When the system is uncertain, it should degrade into simple guidance, not inscrutable failure. + +## Included in this update + +- orchestration state models +- onboarding/session planning scaffold +- learner run-loop scaffold +- stop/claim-readiness criteria scaffold +- UX-oriented recommendation formatting +- sample CLI flow +- UX notes for future web UI work diff --git a/.update_readmes/20260314_132051__230-didactopus-pack-persistence-update__README.md b/.update_readmes/20260314_132051__230-didactopus-pack-persistence-update__README.md new file mode 100644 index 0000000..2da62f8 --- /dev/null +++ b/.update_readmes/20260314_132051__230-didactopus-pack-persistence-update__README.md @@ -0,0 +1,43 @@ +# Didactopus Pack + Persistence Prototype + +This update connects the learner-facing prototype to: + +- **real pack-shaped data files** +- **pack compliance / attribution manifests** +- **persistent learner state** via browser local storage +- a small **Python pack export utility** that converts a Didactopus-style pack directory + into a frontend-consumable JSON bundle + +## What is included + +### Frontend +- topic/domain selection from real pack files +- first-session onboarding from pack metadata +- recommendation cards driven by live learner state +- mastery-map progress from pack concepts and persisted learner records +- milestone/reward feedback +- transparent "why this is recommended" explanations +- compliance/provenance display from pack manifest +- persistent learner state across reloads via local storage + +### Backend-adjacent tooling +- `pack_to_frontend.py` converts: + - `pack.yaml` + - `concepts.yaml` + - `pack_compliance_manifest.json` + into a bundle suitable for the learner UI + +## Why this matters + +This gets Didactopus closer to a usable human-facing system: +- the UI is no longer a static mock +- packs are loadable artifacts +- learner progress persists between sessions +- provenance/compliance data can be shown from real manifests + +## Next likely step + +Add a real API layer so that: +- learner state is persisted outside the browser +- evaluator runs produce evidence automatically +- multiple users can work against the same pack registry diff --git a/.update_readmes/20260314_132053__235-didactopus-productionization-scaffold__README.md b/.update_readmes/20260314_132053__235-didactopus-productionization-scaffold__README.md new file mode 100644 index 0000000..5ea5816 --- /dev/null +++ b/.update_readmes/20260314_132053__235-didactopus-productionization-scaffold__README.md @@ -0,0 +1,30 @@ +# Didactopus Productionization Scaffold + +This update takes the prior authenticated API/database prototype one step closer to a +production-ready shape. + +## Added in this scaffold + +- PostgreSQL-first configuration +- JWT-style auth scaffold with access/refresh token concepts +- role-based authorization model +- background worker queue scaffold +- evaluator history endpoints +- learner-management endpoints +- pack-administration endpoints +- Docker Compose layout for API + worker + PostgreSQL + +## Important note + +This is still a scaffold, not a hardened deployment: +- JWT signing secrets are placeholder-driven +- queue processing is still simplified +- no TLS termination is included here +- migrations are not fully implemented + +## Intended next steps + +- replace placeholders with deployment secrets +- add Alembic migrations +- add Redis-backed queue or a more robust worker setup +- connect the learner UI to the new admin/evaluator-history endpoints diff --git a/.update_readmes/20260314_132058__240-didactopus-review-governance-layer__README.md b/.update_readmes/20260314_132058__240-didactopus-review-governance-layer__README.md new file mode 100644 index 0000000..d692aae --- /dev/null +++ b/.update_readmes/20260314_132058__240-didactopus-review-governance-layer__README.md @@ -0,0 +1,39 @@ +# Didactopus Review Governance Layer + +This update extends the admin-curation scaffold with a **review and governance layer** +for contributed and curated packs. + +## Added in this scaffold + +- pack versioning records +- draft / in_review / approved / rejected publication states +- reviewer comments and sign-off records +- moderation workflow for contributed packs +- admin UI views for governance and review history + +## Why this matters + +Once Didactopus accepts contributed packs or substantial revisions, it needs more than +editing and inspection. It needs process. + +A governance-capable system should let maintainers: +- see what version of a pack is current +- review proposed updates before publication +- record reviewer comments +- approve or reject submissions explicitly +- preserve an audit trail of those actions + +## Scope + +This remains a scaffold: +- versioning is simple and linear +- moderation states are explicit but minimal +- audit history is prototype-level +- approval logic is not yet policy-driven + +## Strong next step + +- connect governance to the full QA pipeline +- require validation/provenance checks before approval +- add multi-reviewer policies and required approvals +- support diff views between pack versions diff --git a/.update_readmes/20260314_132101__245-didactopus-agent-audit-and-key-rotation-layer__README.md b/.update_readmes/20260314_132101__245-didactopus-agent-audit-and-key-rotation-layer__README.md new file mode 100644 index 0000000..937f2ae --- /dev/null +++ b/.update_readmes/20260314_132101__245-didactopus-agent-audit-and-key-rotation-layer__README.md @@ -0,0 +1,27 @@ +# Didactopus Agent Audit Logging + Key Rotation Layer + +This update extends the service-account scaffold with two operational controls: + +- **audit logging** for machine-initiated activity +- **key rotation / revocation scaffolding** for service accounts + +## Added in this scaffold + +- audit log records for service-account actions +- request-level audit helper for agent operations +- service-account secret rotation endpoint +- service-account enable/disable endpoint +- admin UI for viewing audit events and rotating credentials + +## Why this matters + +A serious AI learner deployment needs more than scoped credentials. + +It also needs to answer: + +- which service account did what? +- when did it do it? +- what endpoint or workflow did it invoke? +- can we replace or revoke a compromised credential? + +This layer makes service-account usage more accountable and more maintainable. diff --git a/.update_readmes/20260314_132104__250-didactopus-artifact-lifecycle-and-knowledge-export-layer__README.md b/.update_readmes/20260314_132104__250-didactopus-artifact-lifecycle-and-knowledge-export-layer__README.md new file mode 100644 index 0000000..b6e5a93 --- /dev/null +++ b/.update_readmes/20260314_132104__250-didactopus-artifact-lifecycle-and-knowledge-export-layer__README.md @@ -0,0 +1,62 @@ +# Didactopus Artifact Lifecycle + Knowledge Export Layer + +This update extends the worker-backed artifact registry with: + +- artifact download support +- retention policy support +- artifact expiration metadata +- lifecycle management endpoints +- learner knowledge export paths + +## What it adds + +- artifact download API +- retention policy fields on artifacts +- expiry / purge metadata +- artifact state transitions +- knowledge export scaffolding for reuse beyond Didactopus +- guidance for improving packs, producing curriculum outputs, and generating agent skills + +## Why this matters + +Didactopus should not merely *track* artifacts. It should help manage their lifecycle +and make the knowledge represented by learner activity reusable. + +This layer supports two complementary goals: + +### 1. Artifact lifecycle management +Artifacts can now be: +- registered +- listed +- downloaded +- marked for retention or expiry +- reviewed for later cleanup + +### 2. Knowledge export and reuse +Learner progress can be rendered into structured outputs that may be useful for: +- improving Didactopus domain packs +- drafting traditional curriculum materials +- producing AI-oriented skill packages +- documenting surprising learner discoveries +- supporting mentor review and knowledge capture + +## Knowledge export philosophy + +A learner should not only consume domain packs; sometimes the learner contributes new +understanding, better examples, clearer misconceptions, or unexpected conceptual links. + +Didactopus therefore needs a path from learner activity to reusable artifacts such as: + +- concept observations +- misconception notes +- pack-improvement suggestions +- curriculum outlines +- skill manifests +- structured knowledge snapshots + +## Strong next step + +- true scheduled retention cleanup worker +- signed or permission-checked download tokens +- richer learner-knowledge synthesis pipeline +- export templates for curriculum and skill packages diff --git a/.update_readmes/20260314_132106__255-didactopus-docs-update__README.md b/.update_readmes/20260314_132106__255-didactopus-docs-update__README.md new file mode 100644 index 0000000..d157203 --- /dev/null +++ b/.update_readmes/20260314_132106__255-didactopus-docs-update__README.md @@ -0,0 +1,181 @@ + +# Didactopus + +Didactopus is an experimental learning infrastructure designed to support **human learners, AI learners, and hybrid learning ecosystems**. It focuses on representing knowledge structures, learner progress, and the evolution of understanding in ways that are inspectable, reproducible, and reusable. + +The system treats learning as an **observable graph process** rather than a sequence of isolated exercises. Concept nodes, prerequisite edges, and learner evidence events together produce a dynamic knowledge trajectory. + +Didactopus aims to support: + +- individual mastery learning +- curriculum authoring +- discovery of new conceptual connections +- AI‑assisted autodidactic learning +- generation of reusable educational artifacts + +--- + +# Core Concepts + +## Domain Packs + +A **domain pack** represents a structured set of concepts and relationships. +Concepts form nodes in a graph and may include: + +- prerequisites +- cross‑pack links +- exercises or learning activities +- conceptual metadata + +Domain packs can be: + +- private (learner owned) +- community shared +- curated / mentor‑reviewed + +--- + +## Learner State + +Each learner accumulates **evidence events** that update mastery estimates for concepts. + +Evidence events can include: + +- exercises +- reviews +- projects +- observations +- mentor evaluation + +Mastery records track: + +- score +- confidence +- evidence count +- update history + +The system stores full evidence history so that learning trajectories can be reconstructed. + +--- + +## Artifact System + +Didactopus produces **artifacts** that document learner knowledge and learning trajectories. + +Artifacts may include: + +- animation bundles +- graph visualizations +- knowledge exports +- curriculum drafts +- derived skill descriptions + +Artifacts are tracked using an **artifact registry** with lifecycle metadata. + +Artifact lifecycle states include: + +- created +- retained +- expired +- deleted + +Retention policies allow systems to manage storage while preserving important learner discoveries. + +--- + +# Worker Rendering System + +Rendering jobs transform learner knowledge into visual or structured outputs. + +Typical workflow: + +1. Learner state + pack graph → animation frames +2. Frames exported as SVG +3. Render bundle created +4. Optional FFmpeg render to GIF/MP4 + +Outputs are registered as artifacts so they can be downloaded or reused. + +--- + +# Knowledge Export + +Didactopus supports exporting structured learner knowledge for reuse. + +Export targets include: + +- improved domain packs +- curriculum material +- AI training data +- agent skill definitions +- research on learning processes + +Exports are **candidate knowledge**, not automatically validated truth. +Human mentors or automated validation pipelines can review them before promotion. + +--- + +# Philosophy: Synthesis and Discovery + +Didactopus places strong emphasis on **synthesis**. + +Many important discoveries occur not within a single domain, but at the **intersection of domains**. + +Examples include: + +- mathematics applied to biology +- information theory applied to neuroscience +- physics concepts applied to ecological models + +Domain packs therefore support: + +- cross‑pack links +- relationship annotations +- visualization of conceptual overlap + +These connections help learners discover: + +- analogies +- transferable skills +- deeper structural patterns across knowledge fields + +The goal is not merely to learn isolated facts, but to build a **network of understanding**. + +--- + +# Learners as Discoverers + +Learners sometimes discover insights that mentors did not anticipate. + +Didactopus is designed so that learner output can contribute back into the system through: + +- knowledge export +- artifact review workflows +- pack improvement suggestions + +This creates a **feedback loop** where learning activity improves the curriculum itself. + +--- + +# Intended Uses + +Didactopus supports several categories of use: + +Human learning +- self‑directed study +- classroom support +- mastery‑based curricula + +Research +- studying learning trajectories +- analyzing conceptual difficulty + +AI systems +- training agent skill graphs +- evaluating reasoning development + +Educational publishing +- curriculum drafts +- visualization tools +- learning progress reports + diff --git a/.update_readmes/20260314_132109__260-didactopus-object-versioning-and-export-layer__README.md b/.update_readmes/20260314_132109__260-didactopus-object-versioning-and-export-layer__README.md new file mode 100644 index 0000000..2db87ac --- /dev/null +++ b/.update_readmes/20260314_132109__260-didactopus-object-versioning-and-export-layer__README.md @@ -0,0 +1,33 @@ +# Didactopus Object Editing, Versioning, Merge/Apply, and Export Layer + +This layer extends promotion target objects with: + +- editable downstream objects +- version history for promoted objects +- merge/apply flow for pack patch proposals +- export formats for curriculum drafts and skill bundles + +It adds concrete scaffolding for turning promoted outputs into maintainable assets +rather than one-off records. + +## Added capabilities + +- versioned pack patch proposals +- versioned curriculum drafts +- versioned skill bundles +- patch-apply endpoint for updating pack JSON +- markdown/json export for curriculum drafts +- json/yaml-style manifest export for skill bundles +- reviewer UI prototype for editing and exporting target objects + +## Why this matters + +A promotion target is only the start of the lifecycle. Real use requires: + +- revision +- comparison +- approval +- application +- export + +This scaffold establishes those mechanisms in a minimal but extensible form. diff --git a/.update_readmes/20260314_132112__270-didactopus-promotion-target-objects-layer__README.md b/.update_readmes/20260314_132112__270-didactopus-promotion-target-objects-layer__README.md new file mode 100644 index 0000000..de45dd5 --- /dev/null +++ b/.update_readmes/20260314_132112__270-didactopus-promotion-target-objects-layer__README.md @@ -0,0 +1,21 @@ +# Didactopus Promotion Target Objects Layer + +This layer extends the review workbench and synthesis scaffold by making promotion +targets concrete. Promotions no longer stop at metadata records; they now create +first-class downstream objects. + +Added target object families: + +- pack patch proposals +- curriculum drafts +- skill bundles + +This scaffold includes: + +- ORM models for concrete promotion targets +- repository helpers to create and list them +- promotion logic that materializes target objects +- API endpoints for browsing created target objects +- a UI prototype showing promoted outputs + +This is the bridge between "interesting candidate" and "usable Didactopus asset." diff --git a/.update_readmes/20260314_132115__275-didactopus-review-promotion-and-synthesis-engine__README.md b/.update_readmes/20260314_132115__275-didactopus-review-promotion-and-synthesis-engine__README.md new file mode 100644 index 0000000..5cb2c9f --- /dev/null +++ b/.update_readmes/20260314_132115__275-didactopus-review-promotion-and-synthesis-engine__README.md @@ -0,0 +1,104 @@ +# Didactopus + +Didactopus is an AI-assisted learning and knowledge-graph platform for representing +how understanding develops, how concepts relate, and how learner output can be +reused to improve packs, curricula, and downstream agent skills. + +It is designed for: + +- human learners +- AI learners +- human/AI collaborative learning workflows +- curriculum designers +- mentors and reviewers +- researchers studying learning trajectories + +The system treats learning as a graph process rather than as a sequence of isolated +quiz events. Domain packs define concepts, prerequisites, and cross-pack +relationships. Learner evidence updates mastery estimates and produces reusable +artifacts. + +## Major capabilities + +### Domain packs +Domain packs define concept graphs, prerequisite relationships, and optional +cross-pack links. Packs may be private, shared, reviewed, or published. + +### Learner state +Learners accumulate evidence events, mastery records, evaluation outcomes, and +trajectory histories. + +### Animated graph views +Learning progress can be rendered as stable animated concept graphs and exported +as frame bundles for GIF/MP4 production. + +### Artifact registry +Render bundles, knowledge exports, and derivative outputs are managed as +first-class artifacts with retention metadata and lifecycle controls. + +### Knowledge export +Learner output can be exported as candidate structured knowledge, including: + +- pack-improvement suggestions +- curriculum draft material +- skill-bundle candidates +- archived observations and discovery notes + +### Review and promotion workflow +Learner-derived knowledge is not treated as automatically correct. It enters a +triage and review pipeline where it may be promoted into accepted Didactopus +assets. + +### Synthesis engine +Didactopus emphasizes synthesis: discovering helpful overlaps and structural +analogies between distinct topics. The synthesis engine proposes candidate links, +analogy clusters, and cross-pack insights. + +--- + +## Philosophy + +### Learning as visible structure +The system should make it possible to inspect not just outcomes, but how those +outcomes emerge. + +### Learners as discoverers +Learners sometimes find gaps, hidden prerequisites, better examples, or novel +connections that mentors did not anticipate. Didactopus is designed to capture +that productively. + +### Synthesis matters +Some of the most valuable understanding comes from linking apparently disparate +topics. Didactopus explicitly supports this through: + +- cross-pack links +- similarity scoring +- synthesis proposals +- reusable exports for pack revision and curriculum design + +### Reuse beyond Didactopus +Learner knowledge should be renderable into forms useful for: + +- improved domain packs +- traditional curriculum products +- agentic AI skills +- mentor notes +- research artifacts + +--- + +## New additions in this update + +This update adds design material for: + +- review-and-promotion workflow for learner-derived knowledge +- synthesis engine architecture +- updated README and FAQ language reflecting synthesis and knowledge reuse + +See: + +- `docs/review_and_promotion_workflow.md` +- `docs/synthesis_engine_architecture.md` +- `docs/api_outline.md` +- `docs/data_models.md` +- `FAQ.md` diff --git a/.update_readmes/20260314_132120__280-didactopus-review-workbench-and-synthesis-scaffold__README.md b/.update_readmes/20260314_132120__280-didactopus-review-workbench-and-synthesis-scaffold__README.md new file mode 100644 index 0000000..4efbbee --- /dev/null +++ b/.update_readmes/20260314_132120__280-didactopus-review-workbench-and-synthesis-scaffold__README.md @@ -0,0 +1,15 @@ +# Didactopus Review Workbench + Synthesis Scaffold + +This scaffold turns the review-and-promotion workflow and synthesis-engine design +into a concrete repository layer. + +It adds: + +- ORM models for knowledge candidates, reviews, promotions, and synthesis candidates +- repository helpers for triage, review, promotion, and archival +- API endpoints for the review workflow +- API endpoints for synthesis candidate generation and browsing +- a React UI prototype for a reviewer workbench + +This is still a scaffold rather than a finished production implementation, but it +provides the structural backbone for the next stage of Didactopus. diff --git a/.zip_update_manifest.json b/.zip_update_manifest.json new file mode 100644 index 0000000..b73f93a --- /dev/null +++ b/.zip_update_manifest.json @@ -0,0 +1,8234 @@ +[ + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_131842__085-didactopus-workspace-manager-update__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "3546888cc42c20131f9564e30177a55e71fcafbe275406c39db6a54890140d9b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "workspace_registry.json", + "stripped_member": "workspace_registry.json", + "dest": "/home/netuser/dev/Didactopus/workspace_registry.json", + "action": "create", + "sha256": "645e90afad0d49a75bc5d2c0245ae264a71c5aa169f220b77d1b6f242205284e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "docs/workspace-manager.md", + "stripped_member": "docs/workspace-manager.md", + "dest": "/home/netuser/dev/Didactopus/docs/workspace-manager.md", + "action": "create", + "sha256": "5a43d9523e2a2334fccef43ad8a85ae3626f3066c20cba3e8eb73ab75fba1924", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "docs/faq.md", + "stripped_member": "docs/faq.md", + "dest": "/home/netuser/dev/Didactopus/docs/faq.md", + "action": "overwrite", + "sha256": "eb9674f5764e8bc5f61ec895d3e048ddddf7608cb3c97fa774d0c7c95ab3af06", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "configs/config.example.yaml", + "stripped_member": "configs/config.example.yaml", + "dest": "/home/netuser/dev/Didactopus/configs/config.example.yaml", + "action": "overwrite", + "sha256": "e66c7d5dc5ef0cc52d620fe17719ed285ffc998ee6f9e91352b6bd1ba9d38480", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "131f17f5be2bf3834a84c18c1b27a48885ff666be831a6f0a5a807daadcafb5b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "5b6b42f019f0aa50e8f16d91a8fc3bf228d4b10cc4dbffdf2b499c7415143f0a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "tests/test_workspace_manager.py", + "stripped_member": "tests/test_workspace_manager.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_workspace_manager.py", + "action": "create", + "sha256": "de8c5277331694ba63ab19ef4a203ff67acd68cf58736087e4cdb18bca95e976", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "tests/test_review_bridge.py", + "stripped_member": "tests/test_review_bridge.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_review_bridge.py", + "action": "create", + "sha256": "f6f6b70588f6b72080ddbcde513548e5d0ea8ea62d9b0c092049b3ee2e5c9fb9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "tests/test_webui_files.py", + "stripped_member": "tests/test_webui_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_webui_files.py", + "action": "overwrite", + "sha256": "2375fa9d5d02eace14fcd132f3eb20f221a0ece9d9693372beee3c9e06acbfcf", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "workspaces/stats-foundations/draft_pack/pack.yaml", + "stripped_member": "workspaces/stats-foundations/draft_pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/workspaces/stats-foundations/draft_pack/pack.yaml", + "action": "create", + "sha256": "f948ad0f43d04ba8601177272745f08e3c1fd00637d46b42d8bdcd00feebfeda", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "workspaces/stats-foundations/draft_pack/concepts.yaml", + "stripped_member": "workspaces/stats-foundations/draft_pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/workspaces/stats-foundations/draft_pack/concepts.yaml", + "action": "create", + "sha256": "ebeaffbced7f8fd90d360d1430bcaade3ebdd5199ce7fd027814f8e285ea8603", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "workspaces/stats-foundations/draft_pack/conflict_report.md", + "stripped_member": "workspaces/stats-foundations/draft_pack/conflict_report.md", + "dest": "/home/netuser/dev/Didactopus/workspaces/stats-foundations/draft_pack/conflict_report.md", + "action": "create", + "sha256": "4eb322b1577f1b956fab91d8b4e525891d9c78c712690587396c962b6d8fc582", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "workspaces/stats-foundations/draft_pack/review_report.md", + "stripped_member": "workspaces/stats-foundations/draft_pack/review_report.md", + "dest": "/home/netuser/dev/Didactopus/workspaces/stats-foundations/draft_pack/review_report.md", + "action": "create", + "sha256": "76939f090cdcfe75a60b7ab4718899db8a2057f591fc5830a65e5f1d4a7fb1d9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "workspaces/bayes-intro/draft_pack/pack.yaml", + "stripped_member": "workspaces/bayes-intro/draft_pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/pack.yaml", + "action": "create", + "sha256": "db9490f8df03418cd4bb202300aba0bf0339f4aad48094409a028470f039a4dc", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "workspaces/bayes-intro/draft_pack/concepts.yaml", + "stripped_member": "workspaces/bayes-intro/draft_pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/concepts.yaml", + "action": "create", + "sha256": "ebeaffbced7f8fd90d360d1430bcaade3ebdd5199ce7fd027814f8e285ea8603", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "workspaces/bayes-intro/draft_pack/conflict_report.md", + "stripped_member": "workspaces/bayes-intro/draft_pack/conflict_report.md", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/conflict_report.md", + "action": "create", + "sha256": "4eb322b1577f1b956fab91d8b4e525891d9c78c712690587396c962b6d8fc582", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "workspaces/bayes-intro/draft_pack/review_report.md", + "stripped_member": "workspaces/bayes-intro/draft_pack/review_report.md", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/review_report.md", + "action": "create", + "sha256": "76939f090cdcfe75a60b7ab4718899db8a2057f591fc5830a65e5f1d4a7fb1d9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "73c55856ad1e60177fa61d52a6841b2166b7b3d0a0276b1449cdd33e2bb6e421", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "507cc57f4c603a4ccb5708b3186d1f615d8fd9d6589cd5269268cbef78064106", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "1251cbd55c19eb1d48dd4f8b8167474719e55a7f34ff6bf0b71b34080606bc8e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "91447944015cec709e8aa7655f7e9d64e1e4508e7023a57fe3746911c0fc6fed", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "1af66ae8b252fbeb1953b9e4634abe6d00b30178588738aba38aab38843d0e10", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "src/didactopus/review_schema.py", + "stripped_member": "src/didactopus/review_schema.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_schema.py", + "action": "overwrite", + "sha256": "466cc0764a33010f300a1349e97e95ce6dedc30cab94aa4b2e57ca24082448ec", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "src/didactopus/review_loader.py", + "stripped_member": "src/didactopus/review_loader.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_loader.py", + "action": "overwrite", + "sha256": "34404cbb038ef07651146a3390fa00a88130014f33fe22e261ec4a741c18f129", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "src/didactopus/review_actions.py", + "stripped_member": "src/didactopus/review_actions.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_actions.py", + "action": "overwrite", + "sha256": "ffdfec320bc15726c95bcb7d020b4b8bee4cf5bf6db3c98f69e6a0ab4a53030e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "src/didactopus/review_export.py", + "stripped_member": "src/didactopus/review_export.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_export.py", + "action": "overwrite", + "sha256": "cc591a3766f63cabed0a4f3519fe077e66f2cb246e6c6518601d0c5d0c7a1443", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "src/didactopus/workspace_manager.py", + "stripped_member": "src/didactopus/workspace_manager.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/workspace_manager.py", + "action": "create", + "sha256": "11b9ec10cd61051dba82304793565068088879fb86088099e907daca4acf369c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "src/didactopus/review_bridge.py", + "stripped_member": "src/didactopus/review_bridge.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_bridge.py", + "action": "create", + "sha256": "b8f225af36fb04092557abcb08449c8c83d7c84b1293bb066d463175fe3f1239", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "member": "src/didactopus/review_bridge_server.py", + "stripped_member": "src/didactopus/review_bridge_server.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_bridge_server.py", + "action": "create", + "sha256": "241da89239dcfd3bb5ddb3130e4a3e58a54c6d917b61e0b4ec04ce59864c8ea4", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/085-didactopus-workspace-manager-update.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_131846__090-didactopus-draft-pack-import-workflow-update__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "a8e189b2f2a03e5ebcef830343e6418384f8b03e6b51e32d3495eef68ac90690", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "workspace_registry.json", + "stripped_member": "workspace_registry.json", + "dest": "/home/netuser/dev/Didactopus/workspace_registry.json", + "action": "overwrite", + "sha256": "cfe81a72cb9bb7dda99a541996ab446df493dbf28102c383d79b7b6cc8bd8da1", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "docs/draft-pack-import.md", + "stripped_member": "docs/draft-pack-import.md", + "dest": "/home/netuser/dev/Didactopus/docs/draft-pack-import.md", + "action": "create", + "sha256": "9061167adc97f821fefe05af453703c1291c3520a4cfef1d8017539cc2c7df77", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "docs/faq.md", + "stripped_member": "docs/faq.md", + "dest": "/home/netuser/dev/Didactopus/docs/faq.md", + "action": "overwrite", + "sha256": "c72eb23e2a87e3022b952ff19b5385a0938a183cd751a1bee0d228a36177d30b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "configs/config.example.yaml", + "stripped_member": "configs/config.example.yaml", + "dest": "/home/netuser/dev/Didactopus/configs/config.example.yaml", + "action": "overwrite", + "sha256": "e66c7d5dc5ef0cc52d620fe17719ed285ffc998ee6f9e91352b6bd1ba9d38480", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "131f17f5be2bf3834a84c18c1b27a48885ff666be831a6f0a5a807daadcafb5b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "5b6b42f019f0aa50e8f16d91a8fc3bf228d4b10cc4dbffdf2b499c7415143f0a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "generated-pack/pack.yaml", + "stripped_member": "generated-pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/pack.yaml", + "action": "create", + "sha256": "785c1d84ecbf3960a85e31dc14855d9af9db4975eb23f7ba4f9bb62fbdad74fe", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "generated-pack/concepts.yaml", + "stripped_member": "generated-pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/concepts.yaml", + "action": "create", + "sha256": "3c0cb89f213f3152b8b475a11e163e76eefe7c677752af5676146bfaa1c81060", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "generated-pack/conflict_report.md", + "stripped_member": "generated-pack/conflict_report.md", + "dest": "/home/netuser/dev/Didactopus/generated-pack/conflict_report.md", + "action": "create", + "sha256": "2be76464e24c583c30bf9ec8985fefa427a2c0eb6c31f312fec13ed4dff69c3f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "generated-pack/review_report.md", + "stripped_member": "generated-pack/review_report.md", + "dest": "/home/netuser/dev/Didactopus/generated-pack/review_report.md", + "action": "create", + "sha256": "ba2896556458ad6fb588c6daf0875db46a65e6542437dfc99446c39cb86692b5", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "tests/test_workspace_manager.py", + "stripped_member": "tests/test_workspace_manager.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_workspace_manager.py", + "action": "overwrite", + "sha256": "23b7b7d3339f172efb2c174cd11f6c4ab73eb99c9bdcccc819cd531399e1937e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "tests/test_review_bridge.py", + "stripped_member": "tests/test_review_bridge.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_review_bridge.py", + "action": "overwrite", + "sha256": "f6f6b70588f6b72080ddbcde513548e5d0ea8ea62d9b0c092049b3ee2e5c9fb9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "tests/test_webui_files.py", + "stripped_member": "tests/test_webui_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_webui_files.py", + "action": "overwrite", + "sha256": "2375fa9d5d02eace14fcd132f3eb20f221a0ece9d9693372beee3c9e06acbfcf", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "workspaces/bayes-intro/draft_pack/pack.yaml", + "stripped_member": "workspaces/bayes-intro/draft_pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/pack.yaml", + "action": "overwrite", + "sha256": "db9490f8df03418cd4bb202300aba0bf0339f4aad48094409a028470f039a4dc", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "workspaces/bayes-intro/draft_pack/concepts.yaml", + "stripped_member": "workspaces/bayes-intro/draft_pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/concepts.yaml", + "action": "overwrite", + "sha256": "9003c41ffa2ce00d176e6f12b12ef8e93609317590504f4b6271cf2d6aa555b1", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "workspaces/bayes-intro/draft_pack/conflict_report.md", + "stripped_member": "workspaces/bayes-intro/draft_pack/conflict_report.md", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/conflict_report.md", + "action": "overwrite", + "sha256": "4eb322b1577f1b956fab91d8b4e525891d9c78c712690587396c962b6d8fc582", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "workspaces/bayes-intro/draft_pack/review_report.md", + "stripped_member": "workspaces/bayes-intro/draft_pack/review_report.md", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/review_report.md", + "action": "overwrite", + "sha256": "76939f090cdcfe75a60b7ab4718899db8a2057f591fc5830a65e5f1d4a7fb1d9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "73c55856ad1e60177fa61d52a6841b2166b7b3d0a0276b1449cdd33e2bb6e421", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "c99a16d844df4fcd44dc69a3d2f055ec0c9ca24e3b0c2090f01d0523851f2616", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "7a0bd6eff4b169b3f7171685e7e1e5358003f82742685fc52d9ad6938974cefe", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "91447944015cec709e8aa7655f7e9d64e1e4508e7023a57fe3746911c0fc6fed", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "1af66ae8b252fbeb1953b9e4634abe6d00b30178588738aba38aab38843d0e10", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "src/didactopus/review_schema.py", + "stripped_member": "src/didactopus/review_schema.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_schema.py", + "action": "overwrite", + "sha256": "466cc0764a33010f300a1349e97e95ce6dedc30cab94aa4b2e57ca24082448ec", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "src/didactopus/review_loader.py", + "stripped_member": "src/didactopus/review_loader.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_loader.py", + "action": "overwrite", + "sha256": "34404cbb038ef07651146a3390fa00a88130014f33fe22e261ec4a741c18f129", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "src/didactopus/review_actions.py", + "stripped_member": "src/didactopus/review_actions.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_actions.py", + "action": "overwrite", + "sha256": "ffdfec320bc15726c95bcb7d020b4b8bee4cf5bf6db3c98f69e6a0ab4a53030e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "src/didactopus/review_export.py", + "stripped_member": "src/didactopus/review_export.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_export.py", + "action": "overwrite", + "sha256": "cc591a3766f63cabed0a4f3519fe077e66f2cb246e6c6518601d0c5d0c7a1443", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "src/didactopus/workspace_manager.py", + "stripped_member": "src/didactopus/workspace_manager.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/workspace_manager.py", + "action": "overwrite", + "sha256": "3ed1dcd43e45efeab0e3d5e07143a449abd25b296ef55ea3273d260f0c1f5e5c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "src/didactopus/review_bridge.py", + "stripped_member": "src/didactopus/review_bridge.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_bridge.py", + "action": "overwrite", + "sha256": "b8f225af36fb04092557abcb08449c8c83d7c84b1293bb066d463175fe3f1239", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "member": "src/didactopus/review_bridge_server.py", + "stripped_member": "src/didactopus/review_bridge_server.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_bridge_server.py", + "action": "overwrite", + "sha256": "38cdc841bff20aba9bf641bdb6666639d01a5563aed3183e31f267af138f5d0a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/090-didactopus-draft-pack-import-workflow-update.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_131849__095-didactopus-import-validation-safety-update__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "bf22ce78edb17a25b7667cc32bda2b1180a0568bd23d78411c299b84fb9a1c9c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "workspace_registry.json", + "stripped_member": "workspace_registry.json", + "dest": "/home/netuser/dev/Didactopus/workspace_registry.json", + "action": "overwrite", + "sha256": "cfe81a72cb9bb7dda99a541996ab446df493dbf28102c383d79b7b6cc8bd8da1", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "docs/import-validation.md", + "stripped_member": "docs/import-validation.md", + "dest": "/home/netuser/dev/Didactopus/docs/import-validation.md", + "action": "create", + "sha256": "2e34fa37835c9ab36db46ccc46b8c5d230b7328179a86d0a10eff1799500458b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "docs/faq.md", + "stripped_member": "docs/faq.md", + "dest": "/home/netuser/dev/Didactopus/docs/faq.md", + "action": "overwrite", + "sha256": "0431dd42f51f1122386b481c338b0ccb7ce03a21fd4e4c857501d0c22cbe25b4", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "configs/config.example.yaml", + "stripped_member": "configs/config.example.yaml", + "dest": "/home/netuser/dev/Didactopus/configs/config.example.yaml", + "action": "overwrite", + "sha256": "e66c7d5dc5ef0cc52d620fe17719ed285ffc998ee6f9e91352b6bd1ba9d38480", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "131f17f5be2bf3834a84c18c1b27a48885ff666be831a6f0a5a807daadcafb5b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "5b6b42f019f0aa50e8f16d91a8fc3bf228d4b10cc4dbffdf2b499c7415143f0a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "generated-pack/pack.yaml", + "stripped_member": "generated-pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/pack.yaml", + "action": "overwrite", + "sha256": "785c1d84ecbf3960a85e31dc14855d9af9db4975eb23f7ba4f9bb62fbdad74fe", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "generated-pack/concepts.yaml", + "stripped_member": "generated-pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/concepts.yaml", + "action": "overwrite", + "sha256": "3c0cb89f213f3152b8b475a11e163e76eefe7c677752af5676146bfaa1c81060", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "generated-pack/conflict_report.md", + "stripped_member": "generated-pack/conflict_report.md", + "dest": "/home/netuser/dev/Didactopus/generated-pack/conflict_report.md", + "action": "overwrite", + "sha256": "2be76464e24c583c30bf9ec8985fefa427a2c0eb6c31f312fec13ed4dff69c3f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "generated-pack/review_report.md", + "stripped_member": "generated-pack/review_report.md", + "dest": "/home/netuser/dev/Didactopus/generated-pack/review_report.md", + "action": "overwrite", + "sha256": "ba2896556458ad6fb588c6daf0875db46a65e6542437dfc99446c39cb86692b5", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "bad-generated-pack/pack.yaml", + "stripped_member": "bad-generated-pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/pack.yaml", + "action": "create", + "sha256": "3204cfd69d466b97eb74c8b7cc1bc6e212eab845d646db1b88212efd6a173292", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "tests/test_import_validator.py", + "stripped_member": "tests/test_import_validator.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_import_validator.py", + "action": "create", + "sha256": "6c22499e1328254a6af284bf40ca7bce600bb37e10076f9609a8b89d0c6a72b2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "tests/test_workspace_manager.py", + "stripped_member": "tests/test_workspace_manager.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_workspace_manager.py", + "action": "overwrite", + "sha256": "43d226e6dc98463be5e0cc8f35c9dbf13416a706a31ffe2c2b5df8d44d5ad38e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "tests/test_webui_files.py", + "stripped_member": "tests/test_webui_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_webui_files.py", + "action": "overwrite", + "sha256": "2375fa9d5d02eace14fcd132f3eb20f221a0ece9d9693372beee3c9e06acbfcf", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "workspaces/bayes-intro/draft_pack/pack.yaml", + "stripped_member": "workspaces/bayes-intro/draft_pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/pack.yaml", + "action": "overwrite", + "sha256": "db9490f8df03418cd4bb202300aba0bf0339f4aad48094409a028470f039a4dc", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "workspaces/bayes-intro/draft_pack/concepts.yaml", + "stripped_member": "workspaces/bayes-intro/draft_pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/concepts.yaml", + "action": "overwrite", + "sha256": "9003c41ffa2ce00d176e6f12b12ef8e93609317590504f4b6271cf2d6aa555b1", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "workspaces/bayes-intro/draft_pack/conflict_report.md", + "stripped_member": "workspaces/bayes-intro/draft_pack/conflict_report.md", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/conflict_report.md", + "action": "overwrite", + "sha256": "4eb322b1577f1b956fab91d8b4e525891d9c78c712690587396c962b6d8fc582", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "workspaces/bayes-intro/draft_pack/review_report.md", + "stripped_member": "workspaces/bayes-intro/draft_pack/review_report.md", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/review_report.md", + "action": "overwrite", + "sha256": "76939f090cdcfe75a60b7ab4718899db8a2057f591fc5830a65e5f1d4a7fb1d9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "73c55856ad1e60177fa61d52a6841b2166b7b3d0a0276b1449cdd33e2bb6e421", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "bba1e7c8ed5ccf07b1f1223caf34911ade11963cfff8213eca85510e19638d91", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "8bb2f1bad6cc528c38e05e608eb7974401149b33c908353f711d3d5b87e9bef2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "91447944015cec709e8aa7655f7e9d64e1e4508e7023a57fe3746911c0fc6fed", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "1f62ae21e764adb30578a23e59ee0676a29d103ced208c8c06b303ca4e395190", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "src/didactopus/review_schema.py", + "stripped_member": "src/didactopus/review_schema.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_schema.py", + "action": "overwrite", + "sha256": "c7eca0808c6f4be86bd8231a2ac3baf87dca8e0431e716b7f8840abb5d2fe71a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "src/didactopus/review_loader.py", + "stripped_member": "src/didactopus/review_loader.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_loader.py", + "action": "overwrite", + "sha256": "34404cbb038ef07651146a3390fa00a88130014f33fe22e261ec4a741c18f129", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "src/didactopus/review_actions.py", + "stripped_member": "src/didactopus/review_actions.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_actions.py", + "action": "overwrite", + "sha256": "ffdfec320bc15726c95bcb7d020b4b8bee4cf5bf6db3c98f69e6a0ab4a53030e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "src/didactopus/review_export.py", + "stripped_member": "src/didactopus/review_export.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_export.py", + "action": "overwrite", + "sha256": "cc591a3766f63cabed0a4f3519fe077e66f2cb246e6c6518601d0c5d0c7a1443", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "src/didactopus/import_validator.py", + "stripped_member": "src/didactopus/import_validator.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/import_validator.py", + "action": "create", + "sha256": "10ad5be8295441fdc862abf2e62a11c90511a0f29456a2f5e8c0eadb45f1e7ca", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "src/didactopus/workspace_manager.py", + "stripped_member": "src/didactopus/workspace_manager.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/workspace_manager.py", + "action": "overwrite", + "sha256": "7bc9b122d8c12a7375b856701a595cbc13695c17237124794a4eda4139871465", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "src/didactopus/review_bridge.py", + "stripped_member": "src/didactopus/review_bridge.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_bridge.py", + "action": "overwrite", + "sha256": "b8f225af36fb04092557abcb08449c8c83d7c84b1293bb066d463175fe3f1239", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "member": "src/didactopus/review_bridge_server.py", + "stripped_member": "src/didactopus/review_bridge_server.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_bridge_server.py", + "action": "overwrite", + "sha256": "f67aa049832fbb9df4a34414cf804cf67e76a8486ff574983e88c7d3b74a063f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/095-didactopus-import-validation-safety-update.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_131853__100-didactopus-full-pack-validation-update__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "97838aa6e3ab5a65c0cda6830cd6a5d98c024b54264772ea6495796e3484c3ac", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "workspace_registry.json", + "stripped_member": "workspace_registry.json", + "dest": "/home/netuser/dev/Didactopus/workspace_registry.json", + "action": "overwrite", + "sha256": "cfe81a72cb9bb7dda99a541996ab446df493dbf28102c383d79b7b6cc8bd8da1", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "docs/full-pack-validation.md", + "stripped_member": "docs/full-pack-validation.md", + "dest": "/home/netuser/dev/Didactopus/docs/full-pack-validation.md", + "action": "create", + "sha256": "ef8890357247f41f0374414c67ce7c8c13cb45db5326d7b933b9ce0acab84c77", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "docs/faq.md", + "stripped_member": "docs/faq.md", + "dest": "/home/netuser/dev/Didactopus/docs/faq.md", + "action": "overwrite", + "sha256": "e8024482f879c4d858c85166197d57a24bba2b435a688a4b7bf29e8d3029d22f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "configs/config.example.yaml", + "stripped_member": "configs/config.example.yaml", + "dest": "/home/netuser/dev/Didactopus/configs/config.example.yaml", + "action": "overwrite", + "sha256": "e66c7d5dc5ef0cc52d620fe17719ed285ffc998ee6f9e91352b6bd1ba9d38480", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "131f17f5be2bf3834a84c18c1b27a48885ff666be831a6f0a5a807daadcafb5b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "5b6b42f019f0aa50e8f16d91a8fc3bf228d4b10cc4dbffdf2b499c7415143f0a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "generated-pack/pack.yaml", + "stripped_member": "generated-pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/pack.yaml", + "action": "overwrite", + "sha256": "785c1d84ecbf3960a85e31dc14855d9af9db4975eb23f7ba4f9bb62fbdad74fe", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "generated-pack/concepts.yaml", + "stripped_member": "generated-pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/concepts.yaml", + "action": "overwrite", + "sha256": "3c0cb89f213f3152b8b475a11e163e76eefe7c677752af5676146bfaa1c81060", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "generated-pack/roadmap.yaml", + "stripped_member": "generated-pack/roadmap.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/roadmap.yaml", + "action": "create", + "sha256": "dd366aec07ac74a2af080742f210b0e74607916363abc9c9991de3b3fce4c942", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "generated-pack/projects.yaml", + "stripped_member": "generated-pack/projects.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/projects.yaml", + "action": "create", + "sha256": "90a9801c24a0a32390392abcb088407249cf9811de366cc10f52e2211a4f4114", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "generated-pack/rubrics.yaml", + "stripped_member": "generated-pack/rubrics.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/rubrics.yaml", + "action": "create", + "sha256": "a0e0a2e88f009bbdc60c59f02b9135baea04e7ea86e0707961526268f175aa97", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "generated-pack/conflict_report.md", + "stripped_member": "generated-pack/conflict_report.md", + "dest": "/home/netuser/dev/Didactopus/generated-pack/conflict_report.md", + "action": "overwrite", + "sha256": "2be76464e24c583c30bf9ec8985fefa427a2c0eb6c31f312fec13ed4dff69c3f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "generated-pack/review_report.md", + "stripped_member": "generated-pack/review_report.md", + "dest": "/home/netuser/dev/Didactopus/generated-pack/review_report.md", + "action": "overwrite", + "sha256": "ba2896556458ad6fb588c6daf0875db46a65e6542437dfc99446c39cb86692b5", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "bad-generated-pack/pack.yaml", + "stripped_member": "bad-generated-pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/pack.yaml", + "action": "overwrite", + "sha256": "fbcd54425bcf181e5fe763a662b75bb2e79511f13ec32703e31d9c719f4fd7d2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "bad-generated-pack/concepts.yaml", + "stripped_member": "bad-generated-pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/concepts.yaml", + "action": "create", + "sha256": "8c27edd143653756db0373891b380615023fd6364ffc9004138f44b88d432e43", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "bad-generated-pack/roadmap.yaml", + "stripped_member": "bad-generated-pack/roadmap.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/roadmap.yaml", + "action": "create", + "sha256": "1502ba08af0f272301164f4f8f60f7c387acafac1475eba66463f70ee2c5885d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "bad-generated-pack/projects.yaml", + "stripped_member": "bad-generated-pack/projects.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/projects.yaml", + "action": "create", + "sha256": "29e9b0ed4c137889246cb6b2f1d0cce85875224df50cf3ad91c35f5bb26357a6", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "bad-generated-pack/rubrics.yaml", + "stripped_member": "bad-generated-pack/rubrics.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/rubrics.yaml", + "action": "create", + "sha256": "4ffab26a35d922c04dbe75ea557c8f94d59a131912e444b0984e767aecb2cabb", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "tests/test_pack_validator.py", + "stripped_member": "tests/test_pack_validator.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_pack_validator.py", + "action": "create", + "sha256": "26b9f7ce2fcb26c0072b76f04c49943fe8f0078702fd941aa20667cf78f8971f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "tests/test_import_validator.py", + "stripped_member": "tests/test_import_validator.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_import_validator.py", + "action": "overwrite", + "sha256": "af7c758c6f049db754fbb61028dd178eced23011f871bfab754c53db95e908e9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "tests/test_workspace_manager.py", + "stripped_member": "tests/test_workspace_manager.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_workspace_manager.py", + "action": "overwrite", + "sha256": "51aa24058a268cab2de9ab7207301d9d218011fe856c8544e416a507408c4249", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "tests/test_webui_files.py", + "stripped_member": "tests/test_webui_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_webui_files.py", + "action": "overwrite", + "sha256": "2375fa9d5d02eace14fcd132f3eb20f221a0ece9d9693372beee3c9e06acbfcf", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "workspaces/bayes-intro/draft_pack/pack.yaml", + "stripped_member": "workspaces/bayes-intro/draft_pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/pack.yaml", + "action": "overwrite", + "sha256": "db9490f8df03418cd4bb202300aba0bf0339f4aad48094409a028470f039a4dc", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "workspaces/bayes-intro/draft_pack/concepts.yaml", + "stripped_member": "workspaces/bayes-intro/draft_pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/concepts.yaml", + "action": "overwrite", + "sha256": "9003c41ffa2ce00d176e6f12b12ef8e93609317590504f4b6271cf2d6aa555b1", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "workspaces/bayes-intro/draft_pack/conflict_report.md", + "stripped_member": "workspaces/bayes-intro/draft_pack/conflict_report.md", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/conflict_report.md", + "action": "overwrite", + "sha256": "4eb322b1577f1b956fab91d8b4e525891d9c78c712690587396c962b6d8fc582", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "workspaces/bayes-intro/draft_pack/review_report.md", + "stripped_member": "workspaces/bayes-intro/draft_pack/review_report.md", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/review_report.md", + "action": "overwrite", + "sha256": "76939f090cdcfe75a60b7ab4718899db8a2057f591fc5830a65e5f1d4a7fb1d9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "73c55856ad1e60177fa61d52a6841b2166b7b3d0a0276b1449cdd33e2bb6e421", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "2718a9d88e662215395ce308e62524b35a68329ba6668d326048faa84e295066", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "8bb2f1bad6cc528c38e05e608eb7974401149b33c908353f711d3d5b87e9bef2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "91447944015cec709e8aa7655f7e9d64e1e4508e7023a57fe3746911c0fc6fed", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "1f62ae21e764adb30578a23e59ee0676a29d103ced208c8c06b303ca4e395190", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "src/didactopus/review_schema.py", + "stripped_member": "src/didactopus/review_schema.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_schema.py", + "action": "overwrite", + "sha256": "c7eca0808c6f4be86bd8231a2ac3baf87dca8e0431e716b7f8840abb5d2fe71a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "src/didactopus/review_loader.py", + "stripped_member": "src/didactopus/review_loader.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_loader.py", + "action": "overwrite", + "sha256": "34404cbb038ef07651146a3390fa00a88130014f33fe22e261ec4a741c18f129", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "src/didactopus/review_actions.py", + "stripped_member": "src/didactopus/review_actions.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_actions.py", + "action": "overwrite", + "sha256": "ffdfec320bc15726c95bcb7d020b4b8bee4cf5bf6db3c98f69e6a0ab4a53030e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "src/didactopus/review_export.py", + "stripped_member": "src/didactopus/review_export.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_export.py", + "action": "overwrite", + "sha256": "cc591a3766f63cabed0a4f3519fe077e66f2cb246e6c6518601d0c5d0c7a1443", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "src/didactopus/pack_validator.py", + "stripped_member": "src/didactopus/pack_validator.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/pack_validator.py", + "action": "create", + "sha256": "2443eace477d96ae54623003e91acb3d732667fca51700098a3216802598211a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "src/didactopus/import_validator.py", + "stripped_member": "src/didactopus/import_validator.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/import_validator.py", + "action": "overwrite", + "sha256": "1bb6cf746c6097d6bcca4c4127267e3a9c22a0573307c44095cd5e8a93f0f4d1", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "src/didactopus/workspace_manager.py", + "stripped_member": "src/didactopus/workspace_manager.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/workspace_manager.py", + "action": "overwrite", + "sha256": "7bc9b122d8c12a7375b856701a595cbc13695c17237124794a4eda4139871465", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "src/didactopus/review_bridge.py", + "stripped_member": "src/didactopus/review_bridge.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_bridge.py", + "action": "overwrite", + "sha256": "b8f225af36fb04092557abcb08449c8c83d7c84b1293bb066d463175fe3f1239", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "member": "src/didactopus/review_bridge_server.py", + "stripped_member": "src/didactopus/review_bridge_server.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_bridge_server.py", + "action": "overwrite", + "sha256": "5729ab17cc35da50d211010805f858d788b8f2e3e3562ac0138748e709c76fdb", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/100-didactopus-full-pack-validation-update.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_131856__105-didactopus-coverage-alignment-update__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "e98213b55efa63d712795b7fe6ac6b9d46a290b9f136d67ec185322a070a6c11", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "workspace_registry.json", + "stripped_member": "workspace_registry.json", + "dest": "/home/netuser/dev/Didactopus/workspace_registry.json", + "action": "overwrite", + "sha256": "cfe81a72cb9bb7dda99a541996ab446df493dbf28102c383d79b7b6cc8bd8da1", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "docs/coverage-alignment.md", + "stripped_member": "docs/coverage-alignment.md", + "dest": "/home/netuser/dev/Didactopus/docs/coverage-alignment.md", + "action": "create", + "sha256": "765d4ec7a67cc5c6e0e94c476e3b46f8c4374d21f1d1b6ac67f1479800a66549", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "docs/faq.md", + "stripped_member": "docs/faq.md", + "dest": "/home/netuser/dev/Didactopus/docs/faq.md", + "action": "overwrite", + "sha256": "bfd289badb088b735462ce6a772a451a68e2a7c9e71539d00e092ddfaeaba98a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "configs/config.example.yaml", + "stripped_member": "configs/config.example.yaml", + "dest": "/home/netuser/dev/Didactopus/configs/config.example.yaml", + "action": "overwrite", + "sha256": "20e9a281713d01524aecb1a512fe3395fdb0e5f104fe499bb7424d843387208a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "generated-pack/pack.yaml", + "stripped_member": "generated-pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/pack.yaml", + "action": "overwrite", + "sha256": "785c1d84ecbf3960a85e31dc14855d9af9db4975eb23f7ba4f9bb62fbdad74fe", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "generated-pack/concepts.yaml", + "stripped_member": "generated-pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/concepts.yaml", + "action": "overwrite", + "sha256": "8abab02be347c34bec54bdf3f6e01ee466e1610ff23b2cff8d776e807696a032", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "generated-pack/roadmap.yaml", + "stripped_member": "generated-pack/roadmap.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/roadmap.yaml", + "action": "overwrite", + "sha256": "2947b501823926338981f996277a1e9b7c3c54eb46e41aec9243be266007df25", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "generated-pack/projects.yaml", + "stripped_member": "generated-pack/projects.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/projects.yaml", + "action": "overwrite", + "sha256": "c20abfbf669f6d0a686210e1f49fa9d078676ad75bdab18a4cd47c8a9756dd22", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "generated-pack/rubrics.yaml", + "stripped_member": "generated-pack/rubrics.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/rubrics.yaml", + "action": "overwrite", + "sha256": "98074201601b9649380d24e4a938daaccde5f44a62c293d32182382914d2005a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "bad-generated-pack/pack.yaml", + "stripped_member": "bad-generated-pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/pack.yaml", + "action": "overwrite", + "sha256": "fbcd54425bcf181e5fe763a662b75bb2e79511f13ec32703e31d9c719f4fd7d2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "bad-generated-pack/concepts.yaml", + "stripped_member": "bad-generated-pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/concepts.yaml", + "action": "overwrite", + "sha256": "e5555a690eafbfc05908a37cd6a1f1acd82ba2af8b5b991597814d272aceedea", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "bad-generated-pack/roadmap.yaml", + "stripped_member": "bad-generated-pack/roadmap.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/roadmap.yaml", + "action": "overwrite", + "sha256": "4ae896979cb175437c4d74b92006c74fd248117308e1fc271f9188625f66f8f8", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "bad-generated-pack/projects.yaml", + "stripped_member": "bad-generated-pack/projects.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/projects.yaml", + "action": "overwrite", + "sha256": "bbec01a9505e4ca5437137345135867e0e4b50158276cc623713b47bf3d9aafd", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "bad-generated-pack/rubrics.yaml", + "stripped_member": "bad-generated-pack/rubrics.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/rubrics.yaml", + "action": "overwrite", + "sha256": "97d82f277aade0bcc55145b8b4496f032aaaa30278cb9b4ca2baefbfb1e115b6", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "tests/test_coverage_alignment_qa.py", + "stripped_member": "tests/test_coverage_alignment_qa.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_coverage_alignment_qa.py", + "action": "create", + "sha256": "fca1a926fddbb8f442c90543052ad150933f445c42d8d5aaca7d601bbd76d263", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "tests/test_import_validator.py", + "stripped_member": "tests/test_import_validator.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_import_validator.py", + "action": "overwrite", + "sha256": "817558016b9e9d345d33ef3b9148011f94e6c9ec9d3ce42304f864aa22c1edeb", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "tests/test_webui_files.py", + "stripped_member": "tests/test_webui_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_webui_files.py", + "action": "overwrite", + "sha256": "35c0f1ee58f87393fa21383d28f9bc01f72f4ab407898b913308b1639902a420", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "workspaces/bayes-intro/draft_pack/pack.yaml", + "stripped_member": "workspaces/bayes-intro/draft_pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/pack.yaml", + "action": "overwrite", + "sha256": "db9490f8df03418cd4bb202300aba0bf0339f4aad48094409a028470f039a4dc", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "workspaces/bayes-intro/draft_pack/concepts.yaml", + "stripped_member": "workspaces/bayes-intro/draft_pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/concepts.yaml", + "action": "overwrite", + "sha256": "dfb90cc3da60cc1ee558e98589a6881a6c7fdd29417cc464589e9f38f0900e8b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "workspaces/bayes-intro/draft_pack/conflict_report.md", + "stripped_member": "workspaces/bayes-intro/draft_pack/conflict_report.md", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/conflict_report.md", + "action": "overwrite", + "sha256": "4eb322b1577f1b956fab91d8b4e525891d9c78c712690587396c962b6d8fc582", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "workspaces/bayes-intro/draft_pack/review_report.md", + "stripped_member": "workspaces/bayes-intro/draft_pack/review_report.md", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/review_report.md", + "action": "overwrite", + "sha256": "76939f090cdcfe75a60b7ab4718899db8a2057f591fc5830a65e5f1d4a7fb1d9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "45d8e5addb0bc09ac1f6d0e05f9f8bbb8edf8c5a62372dc0e3fc7088aababd36", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "src/didactopus/review_schema.py", + "stripped_member": "src/didactopus/review_schema.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_schema.py", + "action": "overwrite", + "sha256": "d82258f5b82cc0df30f104c1af88ae44cdc12f0e55745693db116d672c38b163", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "src/didactopus/review_loader.py", + "stripped_member": "src/didactopus/review_loader.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_loader.py", + "action": "overwrite", + "sha256": "1984d9ce82bac0f5c4c69c59b60d6bf78128eb64a8cbc6dc7265405894e44270", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "src/didactopus/review_actions.py", + "stripped_member": "src/didactopus/review_actions.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_actions.py", + "action": "overwrite", + "sha256": "c2b2ef8dd461d773e39d07954878bc3fc9449b48f140dc70aa515e40da325358", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "src/didactopus/review_export.py", + "stripped_member": "src/didactopus/review_export.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_export.py", + "action": "overwrite", + "sha256": "c192b5c34f78c8eaae0832be5527f1d0511e92e19b946e28205fe547ae1caaeb", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "src/didactopus/pack_validator.py", + "stripped_member": "src/didactopus/pack_validator.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/pack_validator.py", + "action": "overwrite", + "sha256": "9465c3e0d036b4d0ac903bf154b5c673207aa48483645844f176dd606788674e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "src/didactopus/semantic_qa.py", + "stripped_member": "src/didactopus/semantic_qa.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/semantic_qa.py", + "action": "create", + "sha256": "9634dc5c618235f60d92694fc33001d014b7f3ffec72c2111606ffd4d745c9b2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "src/didactopus/graph_qa.py", + "stripped_member": "src/didactopus/graph_qa.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/graph_qa.py", + "action": "create", + "sha256": "bb042141f7cbd1f3c817d0937b52a13febff5a5379bdb54bf5137bbac3db88e4", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "src/didactopus/path_quality_qa.py", + "stripped_member": "src/didactopus/path_quality_qa.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/path_quality_qa.py", + "action": "create", + "sha256": "39f9e46d2cb89b8e2ae47f3ddc37af3bb89985d6eb6501abe78aab4dc6c8bca6", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "src/didactopus/coverage_alignment_qa.py", + "stripped_member": "src/didactopus/coverage_alignment_qa.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/coverage_alignment_qa.py", + "action": "create", + "sha256": "ab6c5ccb715c39303b55d4cfeb6ce31c57af2ca543a241e4b368b627a1ff7ea7", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "src/didactopus/import_validator.py", + "stripped_member": "src/didactopus/import_validator.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/import_validator.py", + "action": "overwrite", + "sha256": "74bfc896a2b6df7386c6c0fdeaaa7f13492d6329b91ab85fb54e4b0d9d7f718f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "src/didactopus/workspace_manager.py", + "stripped_member": "src/didactopus/workspace_manager.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/workspace_manager.py", + "action": "overwrite", + "sha256": "f75c0e7c77e19356f563fd96c339f56eb824bc42c24a489fb4d8da178caecda0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "src/didactopus/review_bridge.py", + "stripped_member": "src/didactopus/review_bridge.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_bridge.py", + "action": "overwrite", + "sha256": "57c3dd985da4a919e53173247901a02247a60f96da72d1e5d4c9f3372db125c0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "member": "src/didactopus/review_bridge_server.py", + "stripped_member": "src/didactopus/review_bridge_server.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_bridge_server.py", + "action": "overwrite", + "sha256": "08ff62446af5986e6073025635d4a692d904ab49591a598c3c3979ef5e804760", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/105-didactopus-coverage-alignment-update.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_131859__110-didactopus-curriculum-path-quality-update__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "df2ec366e3b8144766baf179d810cfee8b83ca6148c30beb0e50efc88fe50319", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "workspace_registry.json", + "stripped_member": "workspace_registry.json", + "dest": "/home/netuser/dev/Didactopus/workspace_registry.json", + "action": "overwrite", + "sha256": "cfe81a72cb9bb7dda99a541996ab446df493dbf28102c383d79b7b6cc8bd8da1", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "docs/curriculum-path-quality.md", + "stripped_member": "docs/curriculum-path-quality.md", + "dest": "/home/netuser/dev/Didactopus/docs/curriculum-path-quality.md", + "action": "create", + "sha256": "ff0145e9c5ee09cee8145909258a29347062821d26e9099226a74e2111517e48", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "docs/faq.md", + "stripped_member": "docs/faq.md", + "dest": "/home/netuser/dev/Didactopus/docs/faq.md", + "action": "overwrite", + "sha256": "ca86242c57b1ad6ca217e06347ef9a85bc53572b1f23f53ea629788b8610a719", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "configs/config.example.yaml", + "stripped_member": "configs/config.example.yaml", + "dest": "/home/netuser/dev/Didactopus/configs/config.example.yaml", + "action": "overwrite", + "sha256": "e66c7d5dc5ef0cc52d620fe17719ed285ffc998ee6f9e91352b6bd1ba9d38480", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "6ac1f1a24941ffec5e73385613073ca45d8a48772600b81b414fdbb1ea67ece4", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "4f85e16186bb337e5db91b89ad59a6cb0e4f3e43fbe52a094f6a93b3db1e32e0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "tests/test_path_quality_qa.py", + "stripped_member": "tests/test_path_quality_qa.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_path_quality_qa.py", + "action": "create", + "sha256": "b1f34c68aa19d15099dac20d6d29d6d1b3e6f197180cd222aaf80feba6be19d3", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "tests/test_import_validator.py", + "stripped_member": "tests/test_import_validator.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_import_validator.py", + "action": "overwrite", + "sha256": "76c069e7bbf18fa83daf25dbc4805c5f9eb9055eb7ad21fbf4497942d0413954", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "tests/test_webui_files.py", + "stripped_member": "tests/test_webui_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_webui_files.py", + "action": "overwrite", + "sha256": "35c0f1ee58f87393fa21383d28f9bc01f72f4ab407898b913308b1639902a420", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "generated-pack/pack.yaml", + "stripped_member": "generated-pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/pack.yaml", + "action": "overwrite", + "sha256": "785c1d84ecbf3960a85e31dc14855d9af9db4975eb23f7ba4f9bb62fbdad74fe", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "generated-pack/concepts.yaml", + "stripped_member": "generated-pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/concepts.yaml", + "action": "overwrite", + "sha256": "3301b31fdea2b15960cf5d9ca35057285d97947c534f4f2aa0624726c8d3510f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "generated-pack/roadmap.yaml", + "stripped_member": "generated-pack/roadmap.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/roadmap.yaml", + "action": "overwrite", + "sha256": "2947b501823926338981f996277a1e9b7c3c54eb46e41aec9243be266007df25", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "generated-pack/projects.yaml", + "stripped_member": "generated-pack/projects.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/projects.yaml", + "action": "overwrite", + "sha256": "c89c7b43908072634bbc3fc67c73de831ed8b2ec64fc127daf487360f470321b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "generated-pack/rubrics.yaml", + "stripped_member": "generated-pack/rubrics.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/rubrics.yaml", + "action": "overwrite", + "sha256": "b938cb8eea1f7a786e64bac67d7ec8f4541875fe1cc09b802edd09a945bdf142", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "bad-generated-pack/pack.yaml", + "stripped_member": "bad-generated-pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/pack.yaml", + "action": "overwrite", + "sha256": "fbcd54425bcf181e5fe763a662b75bb2e79511f13ec32703e31d9c719f4fd7d2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "bad-generated-pack/concepts.yaml", + "stripped_member": "bad-generated-pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/concepts.yaml", + "action": "overwrite", + "sha256": "84e638de222cca909bc68044cb74d1f94f5202a817895559e71a09d57da9e21d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "bad-generated-pack/roadmap.yaml", + "stripped_member": "bad-generated-pack/roadmap.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/roadmap.yaml", + "action": "overwrite", + "sha256": "e3b64f25c3f0042e347fb4ed019fbae4cc10f2c1ca2d2cdfd95a1c6b6dd99334", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "bad-generated-pack/projects.yaml", + "stripped_member": "bad-generated-pack/projects.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/projects.yaml", + "action": "overwrite", + "sha256": "659a86180a790a317465148518fb0d1282fb9acf1bf0c93c5cb7e760eab26a14", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "bad-generated-pack/rubrics.yaml", + "stripped_member": "bad-generated-pack/rubrics.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/rubrics.yaml", + "action": "overwrite", + "sha256": "7ea654fec719761416c30dca9dbeb2a03b9e270d30cfb06d3c4ed3ed47549ce0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "workspaces/bayes-intro/draft_pack/pack.yaml", + "stripped_member": "workspaces/bayes-intro/draft_pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/pack.yaml", + "action": "overwrite", + "sha256": "db9490f8df03418cd4bb202300aba0bf0339f4aad48094409a028470f039a4dc", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "workspaces/bayes-intro/draft_pack/concepts.yaml", + "stripped_member": "workspaces/bayes-intro/draft_pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/concepts.yaml", + "action": "overwrite", + "sha256": "dfb90cc3da60cc1ee558e98589a6881a6c7fdd29417cc464589e9f38f0900e8b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "workspaces/bayes-intro/draft_pack/conflict_report.md", + "stripped_member": "workspaces/bayes-intro/draft_pack/conflict_report.md", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/conflict_report.md", + "action": "overwrite", + "sha256": "4eb322b1577f1b956fab91d8b4e525891d9c78c712690587396c962b6d8fc582", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "workspaces/bayes-intro/draft_pack/review_report.md", + "stripped_member": "workspaces/bayes-intro/draft_pack/review_report.md", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/review_report.md", + "action": "overwrite", + "sha256": "76939f090cdcfe75a60b7ab4718899db8a2057f591fc5830a65e5f1d4a7fb1d9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "53d2eee84c6871b3c67ca385d4a4fcc84f4f6c40c89fdc0db52e5219f94bcf4c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "a4e9cd4cf002d3ad6601557cf75c496630c668c011d5eb15869635a93bb60091", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "50c6bd55d70a1004a058063cb0b55d5b5ed82867a0cca79772385f8f778604db", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "91447944015cec709e8aa7655f7e9d64e1e4508e7023a57fe3746911c0fc6fed", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "1f62ae21e764adb30578a23e59ee0676a29d103ced208c8c06b303ca4e395190", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "src/didactopus/review_schema.py", + "stripped_member": "src/didactopus/review_schema.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_schema.py", + "action": "overwrite", + "sha256": "7792512f320872725bb5e965b570eaa1c218d556a1111c830444704cd56f8b5e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "src/didactopus/review_loader.py", + "stripped_member": "src/didactopus/review_loader.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_loader.py", + "action": "overwrite", + "sha256": "34404cbb038ef07651146a3390fa00a88130014f33fe22e261ec4a741c18f129", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "src/didactopus/review_actions.py", + "stripped_member": "src/didactopus/review_actions.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_actions.py", + "action": "overwrite", + "sha256": "ffdfec320bc15726c95bcb7d020b4b8bee4cf5bf6db3c98f69e6a0ab4a53030e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "src/didactopus/review_export.py", + "stripped_member": "src/didactopus/review_export.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_export.py", + "action": "overwrite", + "sha256": "cc591a3766f63cabed0a4f3519fe077e66f2cb246e6c6518601d0c5d0c7a1443", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "src/didactopus/pack_validator.py", + "stripped_member": "src/didactopus/pack_validator.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/pack_validator.py", + "action": "overwrite", + "sha256": "b2fc3c12e555a491c855e9f9588f2c160bc577e891f062e11129c0ba11e72c4e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "src/didactopus/semantic_qa.py", + "stripped_member": "src/didactopus/semantic_qa.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/semantic_qa.py", + "action": "overwrite", + "sha256": "2b189093154f9d791fd1668219b8e6aea10876cdd9cd9389fbb91f4e143b1fcc", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "src/didactopus/graph_qa.py", + "stripped_member": "src/didactopus/graph_qa.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/graph_qa.py", + "action": "overwrite", + "sha256": "40227d6e1d7299b36cf7e65adbf98f298ad51f0c65461b86a809b327adc5f4e4", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "src/didactopus/path_quality_qa.py", + "stripped_member": "src/didactopus/path_quality_qa.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/path_quality_qa.py", + "action": "overwrite", + "sha256": "695c6c7aa49fd522d438563dd701a9700bae7f6e8165b5aedc4b5e01c08a87ba", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "src/didactopus/import_validator.py", + "stripped_member": "src/didactopus/import_validator.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/import_validator.py", + "action": "overwrite", + "sha256": "b60f20b99e8f2d53ab962126c7e1664e081285362ec19476dafcd036a45ca3d2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "src/didactopus/workspace_manager.py", + "stripped_member": "src/didactopus/workspace_manager.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/workspace_manager.py", + "action": "overwrite", + "sha256": "08ae7aab8e6f71d2522455e550aa92ad8cda87f092c8ca9ae7c5e00f3ffa0985", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "src/didactopus/review_bridge.py", + "stripped_member": "src/didactopus/review_bridge.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_bridge.py", + "action": "overwrite", + "sha256": "418b465bb0e11a755351609f6c411d9e1eb4c0f590a739f0a91f73e82b505d51", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "member": "src/didactopus/review_bridge_server.py", + "stripped_member": "src/didactopus/review_bridge_server.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_bridge_server.py", + "action": "overwrite", + "sha256": "a3409e42fc86f2417ecef0775a2a5bfedf01e07d90303f9dd01599e8879deaac", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/110-didactopus-curriculum-path-quality-update.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_131902__115-didactopus-evaluator-alignment-update__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "99220c2e70bbf8159c26ca9be518cd9abffb3f722e47b68b9255df9d1a7d5e8b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "6f45c11f6f49e507102d13f18855ca5665335753369efe32323f945c8f1a384c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "generated-pack/pack.yaml", + "stripped_member": "generated-pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/pack.yaml", + "action": "overwrite", + "sha256": "785c1d84ecbf3960a85e31dc14855d9af9db4975eb23f7ba4f9bb62fbdad74fe", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "generated-pack/concepts.yaml", + "stripped_member": "generated-pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/concepts.yaml", + "action": "overwrite", + "sha256": "93f3051705b4e2db37cc8d3ae22ca7a414e61a067b267f11cb1e153ff5ee6f34", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "generated-pack/roadmap.yaml", + "stripped_member": "generated-pack/roadmap.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/roadmap.yaml", + "action": "overwrite", + "sha256": "c1324318be91ad146a8bb5abc85a704f717df6b66098fe5f0bb04cb009ac27f7", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "generated-pack/projects.yaml", + "stripped_member": "generated-pack/projects.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/projects.yaml", + "action": "overwrite", + "sha256": "1622fe1db0b6eba906af3b06c49616fb315258fc5c59bcdf15f87ba08924241f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "generated-pack/rubrics.yaml", + "stripped_member": "generated-pack/rubrics.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/rubrics.yaml", + "action": "overwrite", + "sha256": "98074201601b9649380d24e4a938daaccde5f44a62c293d32182382914d2005a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "generated-pack/evaluator.yaml", + "stripped_member": "generated-pack/evaluator.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/evaluator.yaml", + "action": "create", + "sha256": "4d5438ee698d8f5da3f14861e3aa251f08e9b30bdd5c777437e0ca59f7e18375", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "bad-generated-pack/pack.yaml", + "stripped_member": "bad-generated-pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/pack.yaml", + "action": "overwrite", + "sha256": "fbcd54425bcf181e5fe763a662b75bb2e79511f13ec32703e31d9c719f4fd7d2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "bad-generated-pack/concepts.yaml", + "stripped_member": "bad-generated-pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/concepts.yaml", + "action": "overwrite", + "sha256": "1a88745df3204a7cfe941acad09b82874297f8fc46d682ee207ad7db25d6a3d1", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "bad-generated-pack/roadmap.yaml", + "stripped_member": "bad-generated-pack/roadmap.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/roadmap.yaml", + "action": "overwrite", + "sha256": "411418162a36222e5f201f57ff9e749b0cfec52ac28ba4799f4ccffbbaee46ba", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "bad-generated-pack/projects.yaml", + "stripped_member": "bad-generated-pack/projects.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/projects.yaml", + "action": "overwrite", + "sha256": "0319283e4f0468ee17d26b7764ba5bc1ec2b210ae8f9a1444efd0ff9efe10b74", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "bad-generated-pack/rubrics.yaml", + "stripped_member": "bad-generated-pack/rubrics.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/rubrics.yaml", + "action": "overwrite", + "sha256": "97d82f277aade0bcc55145b8b4496f032aaaa30278cb9b4ca2baefbfb1e115b6", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "bad-generated-pack/evaluator.yaml", + "stripped_member": "bad-generated-pack/evaluator.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/evaluator.yaml", + "action": "create", + "sha256": "faefcadcd485512206052fdc9cd8abe8016b39dbd658a05ce7cf52f18ee80597", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "tests/test_evaluator_alignment_qa.py", + "stripped_member": "tests/test_evaluator_alignment_qa.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_evaluator_alignment_qa.py", + "action": "create", + "sha256": "dcb6423a40224fa29b8768c9289bf84d5730cec8fc3103870b6fbef361e1fe78", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "tests/test_import_validator.py", + "stripped_member": "tests/test_import_validator.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_import_validator.py", + "action": "overwrite", + "sha256": "a222f42f2c5c734283a0a857cea36a5591bb1e91c75e60169da3f9bca7972779", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "c322570903116d1e6513d7646596c08c03b229d57e12900a2838aa557fa42f3a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "src/didactopus/review_schema.py", + "stripped_member": "src/didactopus/review_schema.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_schema.py", + "action": "overwrite", + "sha256": "bf226a810aeda3d67721c5187c3ba8b1226a959149c6d69a090da4cfa98d6681", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "src/didactopus/pack_validator.py", + "stripped_member": "src/didactopus/pack_validator.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/pack_validator.py", + "action": "overwrite", + "sha256": "50babf54d127899dbe56a0c74fd49f89d740c1a6e82e098ee500c1c7d1382d1b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "src/didactopus/evaluator_alignment_qa.py", + "stripped_member": "src/didactopus/evaluator_alignment_qa.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/evaluator_alignment_qa.py", + "action": "create", + "sha256": "a95bf5c6a2d6d3a25fc18f99aca253a3b3a89eef608d0dcfdcf5b2e8144b367e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "member": "src/didactopus/import_validator.py", + "stripped_member": "src/didactopus/import_validator.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/import_validator.py", + "action": "overwrite", + "sha256": "d45465451e2db5d5b9f0ab667e1cbff1742e6dbcd63f1b45fa5ac946cc1b680a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/115-didactopus-evaluator-alignment-update.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_131906__120-didactopus-evidence-flow-mastery-ledger-update__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "99220c2e70bbf8159c26ca9be518cd9abffb3f722e47b68b9255df9d1a7d5e8b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "6f45c11f6f49e507102d13f18855ca5665335753369efe32323f945c8f1a384c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "generated-pack/pack.yaml", + "stripped_member": "generated-pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/pack.yaml", + "action": "overwrite", + "sha256": "785c1d84ecbf3960a85e31dc14855d9af9db4975eb23f7ba4f9bb62fbdad74fe", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "generated-pack/concepts.yaml", + "stripped_member": "generated-pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/concepts.yaml", + "action": "overwrite", + "sha256": "93f3051705b4e2db37cc8d3ae22ca7a414e61a067b267f11cb1e153ff5ee6f34", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "generated-pack/roadmap.yaml", + "stripped_member": "generated-pack/roadmap.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/roadmap.yaml", + "action": "overwrite", + "sha256": "03c5a22eb1b4568d81e4fa0889797b118f5d775c6ba086931fd1579d942dfdd4", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "generated-pack/projects.yaml", + "stripped_member": "generated-pack/projects.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/projects.yaml", + "action": "overwrite", + "sha256": "4736ce9dfb051bf2fb9fdcdaa28ac424ee1fde4cf9711fdcd9896c68d161f0fb", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "generated-pack/rubrics.yaml", + "stripped_member": "generated-pack/rubrics.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/rubrics.yaml", + "action": "overwrite", + "sha256": "b938cb8eea1f7a786e64bac67d7ec8f4541875fe1cc09b802edd09a945bdf142", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "generated-pack/evaluator.yaml", + "stripped_member": "generated-pack/evaluator.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/evaluator.yaml", + "action": "overwrite", + "sha256": "b54fc37429e0eb6ad3ba62c3abfe5513fcedb6721862291722c689c6098e63f0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "generated-pack/mastery_ledger.yaml", + "stripped_member": "generated-pack/mastery_ledger.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/mastery_ledger.yaml", + "action": "create", + "sha256": "729b412bfc0e5a6e989028e84154fb296b74bbd072f10cf5af2bd3f3fced506c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "bad-generated-pack/pack.yaml", + "stripped_member": "bad-generated-pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/pack.yaml", + "action": "overwrite", + "sha256": "fbcd54425bcf181e5fe763a662b75bb2e79511f13ec32703e31d9c719f4fd7d2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "bad-generated-pack/concepts.yaml", + "stripped_member": "bad-generated-pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/concepts.yaml", + "action": "overwrite", + "sha256": "858bc473bd93b2876fe1c1f274fe63e2420fde7114120f1b7bc37c0fd92a2103", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "bad-generated-pack/roadmap.yaml", + "stripped_member": "bad-generated-pack/roadmap.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/roadmap.yaml", + "action": "overwrite", + "sha256": "7a7564c55455787bc68b8cd0357155a44e6c5cd9eb11cc1f3b140bcd9d3c7257", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "bad-generated-pack/projects.yaml", + "stripped_member": "bad-generated-pack/projects.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/projects.yaml", + "action": "overwrite", + "sha256": "0319283e4f0468ee17d26b7764ba5bc1ec2b210ae8f9a1444efd0ff9efe10b74", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "bad-generated-pack/rubrics.yaml", + "stripped_member": "bad-generated-pack/rubrics.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/rubrics.yaml", + "action": "overwrite", + "sha256": "97d82f277aade0bcc55145b8b4496f032aaaa30278cb9b4ca2baefbfb1e115b6", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "bad-generated-pack/evaluator.yaml", + "stripped_member": "bad-generated-pack/evaluator.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/evaluator.yaml", + "action": "overwrite", + "sha256": "e049e0b88b131729960bcbcca408ba61907e61db6ad53e82a4c599260a38dd49", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "bad-generated-pack/mastery_ledger.yaml", + "stripped_member": "bad-generated-pack/mastery_ledger.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/mastery_ledger.yaml", + "action": "create", + "sha256": "824e394a5d522f6b3480a5e941c3814f81fe1dab97a7c076321459373884e9f6", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "tests/test_evidence_flow_ledger_qa.py", + "stripped_member": "tests/test_evidence_flow_ledger_qa.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_evidence_flow_ledger_qa.py", + "action": "create", + "sha256": "c6ae01e8f3ac2e4b60db8350691ba475dc9f0d0291dc587f9d7a1134f1969729", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "tests/test_import_validator.py", + "stripped_member": "tests/test_import_validator.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_import_validator.py", + "action": "overwrite", + "sha256": "3ff89e9b1fe7fdeed0070a3aaa495852f5855243ea6ea50e4cd89aa2c9f1055b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "753455a9313ec1ea1e13ebb414c6111e48b34abba8f6bc4cc0d9d17ae6d6a45d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "src/didactopus/review_schema.py", + "stripped_member": "src/didactopus/review_schema.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_schema.py", + "action": "overwrite", + "sha256": "ad47bbe764d6c1bdfca741555452c84a3d832f9289f170987cedbc5de54dfa19", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "src/didactopus/pack_validator.py", + "stripped_member": "src/didactopus/pack_validator.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/pack_validator.py", + "action": "overwrite", + "sha256": "326b3a0d853950e09b0e6d35d5a76b62f8a56c10fb080efdea024849ad49ecd9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "src/didactopus/semantic_qa.py", + "stripped_member": "src/didactopus/semantic_qa.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/semantic_qa.py", + "action": "overwrite", + "sha256": "e27499664b95c92777969303d8fb9cf27830e6273e8f894ea270ec45c17631d7", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "src/didactopus/graph_qa.py", + "stripped_member": "src/didactopus/graph_qa.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/graph_qa.py", + "action": "overwrite", + "sha256": "d4d4c3a7dd904ba52ea4fad179688f2904535005544bbefd6ba66b3fe748270e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "src/didactopus/path_quality_qa.py", + "stripped_member": "src/didactopus/path_quality_qa.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/path_quality_qa.py", + "action": "overwrite", + "sha256": "076842184acd27d1da040dbaca1d9ca981973414ee4d0ce3076993d85ac513db", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "src/didactopus/coverage_alignment_qa.py", + "stripped_member": "src/didactopus/coverage_alignment_qa.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/coverage_alignment_qa.py", + "action": "overwrite", + "sha256": "52a743b3cd66149d8283d82ad2d94568329a7424c050f8ae7e47a99759b51722", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "src/didactopus/evaluator_alignment_qa.py", + "stripped_member": "src/didactopus/evaluator_alignment_qa.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/evaluator_alignment_qa.py", + "action": "overwrite", + "sha256": "b37cdff630605a99b51da69444bd453b87f16bf241e6635320f9557595e2dd66", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "src/didactopus/evidence_flow_ledger_qa.py", + "stripped_member": "src/didactopus/evidence_flow_ledger_qa.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/evidence_flow_ledger_qa.py", + "action": "create", + "sha256": "4a9e8e2f4ca9b9b51ccaa6806b8e8338a354863c5c0fb17ed8ed2894aa6d823f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "member": "src/didactopus/import_validator.py", + "stripped_member": "src/didactopus/import_validator.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/import_validator.py", + "action": "overwrite", + "sha256": "4899623ae908d53efdff3b77b98066d93eeda20d109c7d0d74b13c83a192efca", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/120-didactopus-evidence-flow-mastery-ledger-update.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_131909__125-didactopus-graph-prereq-analysis-update__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "d5c0f3b063040966d6d1c464b88f8f32cf120235dcb0266775ddcd5fd5153d09", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "workspace_registry.json", + "stripped_member": "workspace_registry.json", + "dest": "/home/netuser/dev/Didactopus/workspace_registry.json", + "action": "overwrite", + "sha256": "cfe81a72cb9bb7dda99a541996ab446df493dbf28102c383d79b7b6cc8bd8da1", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "docs/graph-prerequisite-analysis.md", + "stripped_member": "docs/graph-prerequisite-analysis.md", + "dest": "/home/netuser/dev/Didactopus/docs/graph-prerequisite-analysis.md", + "action": "create", + "sha256": "36430f8a868820da5476f6735dc18f494d4478a74b1fc2f7a87d68bf3da986c8", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "docs/faq.md", + "stripped_member": "docs/faq.md", + "dest": "/home/netuser/dev/Didactopus/docs/faq.md", + "action": "overwrite", + "sha256": "b50c222a3ac62d90b3aca225a5eb7a691209f2a5428555fe4cdceb64ce47032c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "configs/config.example.yaml", + "stripped_member": "configs/config.example.yaml", + "dest": "/home/netuser/dev/Didactopus/configs/config.example.yaml", + "action": "overwrite", + "sha256": "e66c7d5dc5ef0cc52d620fe17719ed285ffc998ee6f9e91352b6bd1ba9d38480", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "131f17f5be2bf3834a84c18c1b27a48885ff666be831a6f0a5a807daadcafb5b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "5b6b42f019f0aa50e8f16d91a8fc3bf228d4b10cc4dbffdf2b499c7415143f0a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "generated-pack/pack.yaml", + "stripped_member": "generated-pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/pack.yaml", + "action": "overwrite", + "sha256": "785c1d84ecbf3960a85e31dc14855d9af9db4975eb23f7ba4f9bb62fbdad74fe", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "generated-pack/concepts.yaml", + "stripped_member": "generated-pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/concepts.yaml", + "action": "overwrite", + "sha256": "cbadec882929e662e613cc1e3c2a8a4664d5d1d0d5135efad9f14655ffd0ebb1", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "generated-pack/roadmap.yaml", + "stripped_member": "generated-pack/roadmap.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/roadmap.yaml", + "action": "overwrite", + "sha256": "19a041ccae80eccaa34c8d7302a8256c61e7ad7fab59ba147b6db5b8c1814839", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "generated-pack/projects.yaml", + "stripped_member": "generated-pack/projects.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/projects.yaml", + "action": "overwrite", + "sha256": "43e59e981b929ee05d5ede346492eab6e64385ef0e86d9945a6313fe85a2d6f9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "generated-pack/rubrics.yaml", + "stripped_member": "generated-pack/rubrics.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/rubrics.yaml", + "action": "overwrite", + "sha256": "bb9647c5a6e02f1c6962ea7b490869aa1a423b1c481ffb60c7189ed72df6cf9b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "bad-generated-pack/pack.yaml", + "stripped_member": "bad-generated-pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/pack.yaml", + "action": "overwrite", + "sha256": "fbcd54425bcf181e5fe763a662b75bb2e79511f13ec32703e31d9c719f4fd7d2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "bad-generated-pack/concepts.yaml", + "stripped_member": "bad-generated-pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/concepts.yaml", + "action": "overwrite", + "sha256": "a2c8fe81a10ab4acaefff4424446b2d3ef46e2766cc3b1a21e74d7edd916804a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "bad-generated-pack/roadmap.yaml", + "stripped_member": "bad-generated-pack/roadmap.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/roadmap.yaml", + "action": "overwrite", + "sha256": "faa4f898e5bdff1e4d7b1a2d822bd471a877141407daa873215082315367830c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "bad-generated-pack/projects.yaml", + "stripped_member": "bad-generated-pack/projects.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/projects.yaml", + "action": "overwrite", + "sha256": "43e59e981b929ee05d5ede346492eab6e64385ef0e86d9945a6313fe85a2d6f9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "bad-generated-pack/rubrics.yaml", + "stripped_member": "bad-generated-pack/rubrics.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/rubrics.yaml", + "action": "overwrite", + "sha256": "bb9647c5a6e02f1c6962ea7b490869aa1a423b1c481ffb60c7189ed72df6cf9b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "tests/test_graph_qa.py", + "stripped_member": "tests/test_graph_qa.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_graph_qa.py", + "action": "create", + "sha256": "9e145ec68cae5cba22a29e25bdb6958746a86a3217ae101c52d3051cf01a7094", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "tests/test_import_validator.py", + "stripped_member": "tests/test_import_validator.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_import_validator.py", + "action": "overwrite", + "sha256": "af1dab88f764f2fac8155fb5145896909a6693f6bde61b2ad540c993833bbfb5", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "tests/test_pack_validator.py", + "stripped_member": "tests/test_pack_validator.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_pack_validator.py", + "action": "overwrite", + "sha256": "fedca4f4f65bdac2534a0735a5b3730eb26e51d8e8bfbf3d4e4d3170c49986d8", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "tests/test_webui_files.py", + "stripped_member": "tests/test_webui_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_webui_files.py", + "action": "overwrite", + "sha256": "2375fa9d5d02eace14fcd132f3eb20f221a0ece9d9693372beee3c9e06acbfcf", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "workspaces/bayes-intro/draft_pack/pack.yaml", + "stripped_member": "workspaces/bayes-intro/draft_pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/pack.yaml", + "action": "overwrite", + "sha256": "db9490f8df03418cd4bb202300aba0bf0339f4aad48094409a028470f039a4dc", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "workspaces/bayes-intro/draft_pack/concepts.yaml", + "stripped_member": "workspaces/bayes-intro/draft_pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/concepts.yaml", + "action": "overwrite", + "sha256": "dfb90cc3da60cc1ee558e98589a6881a6c7fdd29417cc464589e9f38f0900e8b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "workspaces/bayes-intro/draft_pack/conflict_report.md", + "stripped_member": "workspaces/bayes-intro/draft_pack/conflict_report.md", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/conflict_report.md", + "action": "overwrite", + "sha256": "4eb322b1577f1b956fab91d8b4e525891d9c78c712690587396c962b6d8fc582", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "workspaces/bayes-intro/draft_pack/review_report.md", + "stripped_member": "workspaces/bayes-intro/draft_pack/review_report.md", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/review_report.md", + "action": "overwrite", + "sha256": "76939f090cdcfe75a60b7ab4718899db8a2057f591fc5830a65e5f1d4a7fb1d9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "73c55856ad1e60177fa61d52a6841b2166b7b3d0a0276b1449cdd33e2bb6e421", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "25bdfefe41f53282417a98b6ddc7fe8366beb226ad22b5b39b50874bf51bdbc7", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "7eaf21e0b93516f089ee25e8f5ec7e5f002c913143ca5bc53664ed79f69e8b7c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "91447944015cec709e8aa7655f7e9d64e1e4508e7023a57fe3746911c0fc6fed", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "1f62ae21e764adb30578a23e59ee0676a29d103ced208c8c06b303ca4e395190", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "src/didactopus/review_schema.py", + "stripped_member": "src/didactopus/review_schema.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_schema.py", + "action": "overwrite", + "sha256": "70a1633775743a2675f29634fd9b6c080abcb8394d514f9d7b5eb06e73318ef2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "src/didactopus/review_loader.py", + "stripped_member": "src/didactopus/review_loader.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_loader.py", + "action": "overwrite", + "sha256": "34404cbb038ef07651146a3390fa00a88130014f33fe22e261ec4a741c18f129", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "src/didactopus/review_actions.py", + "stripped_member": "src/didactopus/review_actions.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_actions.py", + "action": "overwrite", + "sha256": "ffdfec320bc15726c95bcb7d020b4b8bee4cf5bf6db3c98f69e6a0ab4a53030e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "src/didactopus/review_export.py", + "stripped_member": "src/didactopus/review_export.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_export.py", + "action": "overwrite", + "sha256": "cc591a3766f63cabed0a4f3519fe077e66f2cb246e6c6518601d0c5d0c7a1443", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "src/didactopus/pack_validator.py", + "stripped_member": "src/didactopus/pack_validator.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/pack_validator.py", + "action": "overwrite", + "sha256": "ccfa8f89becddcf3d1a82952e5ef9f8b057a3187fe47cf16caa295b080544ce1", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "src/didactopus/semantic_qa.py", + "stripped_member": "src/didactopus/semantic_qa.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/semantic_qa.py", + "action": "overwrite", + "sha256": "94f9fd3e34597c93083e5946ff623e67646800e8a98441bcf5934378517a59f7", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "src/didactopus/graph_qa.py", + "stripped_member": "src/didactopus/graph_qa.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/graph_qa.py", + "action": "overwrite", + "sha256": "f2fa547a3a08d3070333642ea39f28ddf582ad8d944d3aa2fde69f239dcac298", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "src/didactopus/import_validator.py", + "stripped_member": "src/didactopus/import_validator.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/import_validator.py", + "action": "overwrite", + "sha256": "240d6d9bd21786e55f29b3a572a7a90dc20131537052dfcf92f34d31133234c3", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "src/didactopus/workspace_manager.py", + "stripped_member": "src/didactopus/workspace_manager.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/workspace_manager.py", + "action": "overwrite", + "sha256": "7bc9b122d8c12a7375b856701a595cbc13695c17237124794a4eda4139871465", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "src/didactopus/review_bridge.py", + "stripped_member": "src/didactopus/review_bridge.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_bridge.py", + "action": "overwrite", + "sha256": "b8f225af36fb04092557abcb08449c8c83d7c84b1293bb066d463175fe3f1239", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "member": "src/didactopus/review_bridge_server.py", + "stripped_member": "src/didactopus/review_bridge_server.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_bridge_server.py", + "action": "overwrite", + "sha256": "50ed38286130dbabfff09d02f11c6981106cd3062472501337ae8bc997cea734", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/125-didactopus-graph-prereq-analysis-update.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_131913__130-didactopus-semantic-qa-update__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "a05b0cd4b46ead45803c08d9fc8dcb83dff1d9b0fde5b6bae5c202dff1c3bd07", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "workspace_registry.json", + "stripped_member": "workspace_registry.json", + "dest": "/home/netuser/dev/Didactopus/workspace_registry.json", + "action": "overwrite", + "sha256": "cfe81a72cb9bb7dda99a541996ab446df493dbf28102c383d79b7b6cc8bd8da1", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "docs/semantic-qa.md", + "stripped_member": "docs/semantic-qa.md", + "dest": "/home/netuser/dev/Didactopus/docs/semantic-qa.md", + "action": "create", + "sha256": "45c82683ed652c9af0d6e5afd6092620c489fb2aa9b4fd46fbb3508636e5ef11", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "docs/faq.md", + "stripped_member": "docs/faq.md", + "dest": "/home/netuser/dev/Didactopus/docs/faq.md", + "action": "overwrite", + "sha256": "142cc51253e6e045870103f68833951b58d350d53d6b45afd3ae85297be0c7a1", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "configs/config.example.yaml", + "stripped_member": "configs/config.example.yaml", + "dest": "/home/netuser/dev/Didactopus/configs/config.example.yaml", + "action": "overwrite", + "sha256": "e66c7d5dc5ef0cc52d620fe17719ed285ffc998ee6f9e91352b6bd1ba9d38480", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "131f17f5be2bf3834a84c18c1b27a48885ff666be831a6f0a5a807daadcafb5b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "5b6b42f019f0aa50e8f16d91a8fc3bf228d4b10cc4dbffdf2b499c7415143f0a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "generated-pack/pack.yaml", + "stripped_member": "generated-pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/pack.yaml", + "action": "overwrite", + "sha256": "785c1d84ecbf3960a85e31dc14855d9af9db4975eb23f7ba4f9bb62fbdad74fe", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "generated-pack/concepts.yaml", + "stripped_member": "generated-pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/concepts.yaml", + "action": "overwrite", + "sha256": "a403a7415b571f7a82715a5aac682383fa979304c49faffadefb02602151416e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "generated-pack/roadmap.yaml", + "stripped_member": "generated-pack/roadmap.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/roadmap.yaml", + "action": "overwrite", + "sha256": "6c60b522822570e7a4cea14794747b28a871b291b90ec5c2431ef50998f8f8cd", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "generated-pack/projects.yaml", + "stripped_member": "generated-pack/projects.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/projects.yaml", + "action": "overwrite", + "sha256": "90a9801c24a0a32390392abcb088407249cf9811de366cc10f52e2211a4f4114", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "generated-pack/rubrics.yaml", + "stripped_member": "generated-pack/rubrics.yaml", + "dest": "/home/netuser/dev/Didactopus/generated-pack/rubrics.yaml", + "action": "overwrite", + "sha256": "a0e0a2e88f009bbdc60c59f02b9135baea04e7ea86e0707961526268f175aa97", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "bad-generated-pack/pack.yaml", + "stripped_member": "bad-generated-pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/pack.yaml", + "action": "overwrite", + "sha256": "fbcd54425bcf181e5fe763a662b75bb2e79511f13ec32703e31d9c719f4fd7d2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "bad-generated-pack/concepts.yaml", + "stripped_member": "bad-generated-pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/concepts.yaml", + "action": "overwrite", + "sha256": "ab378927f182e5472f644b1d4eb3d04d442d1ef2a4eb45e34cd02478cbd6bc3d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "bad-generated-pack/roadmap.yaml", + "stripped_member": "bad-generated-pack/roadmap.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/roadmap.yaml", + "action": "overwrite", + "sha256": "c736601bc1329a97073ef9148155689408f6a12cfbb816cc9a9eee2fa0c943c5", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "bad-generated-pack/projects.yaml", + "stripped_member": "bad-generated-pack/projects.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/projects.yaml", + "action": "overwrite", + "sha256": "43e59e981b929ee05d5ede346492eab6e64385ef0e86d9945a6313fe85a2d6f9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "bad-generated-pack/rubrics.yaml", + "stripped_member": "bad-generated-pack/rubrics.yaml", + "dest": "/home/netuser/dev/Didactopus/bad-generated-pack/rubrics.yaml", + "action": "overwrite", + "sha256": "fe6d44be647db22d4ee46abf32c02c2ffe01559089834fc4939e67788b20267d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "tests/test_semantic_qa.py", + "stripped_member": "tests/test_semantic_qa.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_semantic_qa.py", + "action": "create", + "sha256": "b4c14f2e6d487f3ea54f89445639504c588c401b5ba48bea27d724059b716932", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "tests/test_import_validator.py", + "stripped_member": "tests/test_import_validator.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_import_validator.py", + "action": "overwrite", + "sha256": "264fc3cbfb820ff9093d751563f677bc242dcc64c8b84e90d118018c959a44c7", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "tests/test_pack_validator.py", + "stripped_member": "tests/test_pack_validator.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_pack_validator.py", + "action": "overwrite", + "sha256": "5a51274b83d735634059dbb9089e495f580cd7b4ceb887312692c37bb71d6b07", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "tests/test_webui_files.py", + "stripped_member": "tests/test_webui_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_webui_files.py", + "action": "overwrite", + "sha256": "2375fa9d5d02eace14fcd132f3eb20f221a0ece9d9693372beee3c9e06acbfcf", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "workspaces/bayes-intro/draft_pack/pack.yaml", + "stripped_member": "workspaces/bayes-intro/draft_pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/pack.yaml", + "action": "overwrite", + "sha256": "db9490f8df03418cd4bb202300aba0bf0339f4aad48094409a028470f039a4dc", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "workspaces/bayes-intro/draft_pack/concepts.yaml", + "stripped_member": "workspaces/bayes-intro/draft_pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/concepts.yaml", + "action": "overwrite", + "sha256": "620ef797ee469392b700f6a1f168d62c3d1f765d573a70fc515ba62266417c5d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "workspaces/bayes-intro/draft_pack/conflict_report.md", + "stripped_member": "workspaces/bayes-intro/draft_pack/conflict_report.md", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/conflict_report.md", + "action": "overwrite", + "sha256": "4eb322b1577f1b956fab91d8b4e525891d9c78c712690587396c962b6d8fc582", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "workspaces/bayes-intro/draft_pack/review_report.md", + "stripped_member": "workspaces/bayes-intro/draft_pack/review_report.md", + "dest": "/home/netuser/dev/Didactopus/workspaces/bayes-intro/draft_pack/review_report.md", + "action": "overwrite", + "sha256": "76939f090cdcfe75a60b7ab4718899db8a2057f591fc5830a65e5f1d4a7fb1d9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "73c55856ad1e60177fa61d52a6841b2166b7b3d0a0276b1449cdd33e2bb6e421", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "8f384f0bbd544786d6414671812b4638ea0ccf329617d021242c5f6c98c2243b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "fb181c8e9d0ff8adac10bffd2dec4351e3fbc67689334c9d3d6316e7fd3fbacf", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "91447944015cec709e8aa7655f7e9d64e1e4508e7023a57fe3746911c0fc6fed", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "1f62ae21e764adb30578a23e59ee0676a29d103ced208c8c06b303ca4e395190", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "src/didactopus/review_schema.py", + "stripped_member": "src/didactopus/review_schema.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_schema.py", + "action": "overwrite", + "sha256": "217315546564bc4d9480fac72f11ae512a206fa06699c80d221ff629b9a45392", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "src/didactopus/review_loader.py", + "stripped_member": "src/didactopus/review_loader.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_loader.py", + "action": "overwrite", + "sha256": "34404cbb038ef07651146a3390fa00a88130014f33fe22e261ec4a741c18f129", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "src/didactopus/review_actions.py", + "stripped_member": "src/didactopus/review_actions.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_actions.py", + "action": "overwrite", + "sha256": "ffdfec320bc15726c95bcb7d020b4b8bee4cf5bf6db3c98f69e6a0ab4a53030e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "src/didactopus/review_export.py", + "stripped_member": "src/didactopus/review_export.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_export.py", + "action": "overwrite", + "sha256": "cc591a3766f63cabed0a4f3519fe077e66f2cb246e6c6518601d0c5d0c7a1443", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "src/didactopus/pack_validator.py", + "stripped_member": "src/didactopus/pack_validator.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/pack_validator.py", + "action": "overwrite", + "sha256": "ccfa8f89becddcf3d1a82952e5ef9f8b057a3187fe47cf16caa295b080544ce1", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "src/didactopus/semantic_qa.py", + "stripped_member": "src/didactopus/semantic_qa.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/semantic_qa.py", + "action": "overwrite", + "sha256": "39623ebe406dfcbb763d5dc22e6006a81cd0e6b41c564884345bd13b6fe87d19", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "src/didactopus/import_validator.py", + "stripped_member": "src/didactopus/import_validator.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/import_validator.py", + "action": "overwrite", + "sha256": "b4429aa04d02b6f5d1fd4430f4bb18e5a7d43276ed8762499bb830da050cadfd", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "src/didactopus/workspace_manager.py", + "stripped_member": "src/didactopus/workspace_manager.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/workspace_manager.py", + "action": "overwrite", + "sha256": "7bc9b122d8c12a7375b856701a595cbc13695c17237124794a4eda4139871465", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "src/didactopus/review_bridge.py", + "stripped_member": "src/didactopus/review_bridge.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_bridge.py", + "action": "overwrite", + "sha256": "b8f225af36fb04092557abcb08449c8c83d7c84b1293bb066d463175fe3f1239", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "member": "src/didactopus/review_bridge_server.py", + "stripped_member": "src/didactopus/review_bridge_server.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/review_bridge_server.py", + "action": "overwrite", + "sha256": "bdf36c33171ad8ecda29c2eae8fcf41bd869d7aee0a459fb7a42dba7895a2a03", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/130-didactopus-semantic-qa-update.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_131917__135-didactopus-admin-curation-layer__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "ed3405034f233d4319d9a34b3cc2ecca587c481362afcd585b9c656f506da798", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "a8fb7e091680f90c1263686f5e032a218623228db18fa7ff8f7745e72a136819", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "af6737bd4bc5873578ee27fc3420881988c305d64ca7f48d9434e027323b8fbc", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "tests/test_scaffold_files.py", + "stripped_member": "tests/test_scaffold_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_scaffold_files.py", + "action": "create", + "sha256": "422ed2af486b957f852c599c8c779d7838711f1d9f0ad9c1e03a5d223544753a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "53d2eee84c6871b3c67ca385d4a4fcc84f4f6c40c89fdc0db52e5219f94bcf4c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "webui/src/api.js", + "stripped_member": "webui/src/api.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/api.js", + "action": "create", + "sha256": "9042eaec7d8690047a52ca8133a10b2b83595736f8fe93de1ff465ef72aaee0b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "webui/src/authStore.js", + "stripped_member": "webui/src/authStore.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/authStore.js", + "action": "create", + "sha256": "d26a0e35b95996613ef24574fdfc8d97bfd2df9d8ef9c1cff5833461d162c4a0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "3357f79d021e6c5d211a5393fc336543d464fb05854c3b2b0f3a20a6b258f16b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "4163992a019ffaebe78562e446fa015141d2667a9676d6f8329c01367259e03f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "23d092311fbefc5999518a7b92718e7435835c70839e663171b646bdba8006cd", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "src/didactopus/db.py", + "stripped_member": "src/didactopus/db.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/db.py", + "action": "create", + "sha256": "5773454c709c7288aed68dbd73e3134deececb328214ff5cf649a51fd0ea7790", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "src/didactopus/orm.py", + "stripped_member": "src/didactopus/orm.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orm.py", + "action": "create", + "sha256": "5dccc5c757bc249a64e11629ad333267f9059679ddf2da1dc78a69568e276521", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "create", + "sha256": "6c603508dd85170787e7cb156441cde4f3ce9e5d83100a228fbc94abb1f75c8d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "src/didactopus/auth.py", + "stripped_member": "src/didactopus/auth.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/auth.py", + "action": "create", + "sha256": "554a347973d3beec8511c99074a583d9679c7d06d84b5573df4220c75830bd23", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "src/didactopus/engine.py", + "stripped_member": "src/didactopus/engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/engine.py", + "action": "create", + "sha256": "16dadf1b4f917b1b2c67edfbf8bf494c6753c3d6269778832daeea8795a4022d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "src/didactopus/repository.py", + "stripped_member": "src/didactopus/repository.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/repository.py", + "action": "create", + "sha256": "c5549f9ee14740c49a87f4fa293b4c4d7ea341ce97e46a2979ea49fca4489ba4", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "src/didactopus/worker.py", + "stripped_member": "src/didactopus/worker.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/worker.py", + "action": "create", + "sha256": "4dc2ad94224943d12846b513aec761361f71340b2731a45957e7c38f0f8ac93e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "src/didactopus/seed.py", + "stripped_member": "src/didactopus/seed.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/seed.py", + "action": "create", + "sha256": "091783218c68e132b5850c94a18b5019bf33c70f11b465e825b1c57e81dae4a7", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "create", + "sha256": "0f64b0219914982497ed6eb7b6049125d019af29122aa564235858dc2a1e76fa", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/135-didactopus-admin-curation-layer.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_131920__140-didactopus-admin-learner-ui-workflows__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "ed3405034f233d4319d9a34b3cc2ecca587c481362afcd585b9c656f506da798", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "6278808b5f99fc82e66b14a8534bebcde287a3243ff7b48d5e7fbe1ca0e717ad", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "0be9918d731d61cd1598a58afa56aea1113568b433b67c159a097c89724de0aa", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "tests/test_scaffold_files.py", + "stripped_member": "tests/test_scaffold_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_scaffold_files.py", + "action": "overwrite", + "sha256": "422ed2af486b957f852c599c8c779d7838711f1d9f0ad9c1e03a5d223544753a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "73c55856ad1e60177fa61d52a6841b2166b7b3d0a0276b1449cdd33e2bb6e421", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "webui/src/api.js", + "stripped_member": "webui/src/api.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/api.js", + "action": "overwrite", + "sha256": "7e4b544c6cbc0195a5b2da1204d40e6abfb672b72d2f3e4a7ffcf087435dc1d3", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "webui/src/authStore.js", + "stripped_member": "webui/src/authStore.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/authStore.js", + "action": "overwrite", + "sha256": "7f94abf009d256545651633e59ed3b7e981680d41f58a22406a103529ba4fbcf", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "8260c1502b9966ac89e875a77cea52d61d7e7a91f8d489129a7b7ade4c8c67a1", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "ef02d0165aadfde9784fb8470f181a67b10a78873048311c954fc2cd4f5c1af6", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "69e78652baa89c5a55aa9d70aff05fbb658502107546965ecba1e3abae0f14db", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "src/didactopus/db.py", + "stripped_member": "src/didactopus/db.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/db.py", + "action": "overwrite", + "sha256": "5773454c709c7288aed68dbd73e3134deececb328214ff5cf649a51fd0ea7790", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "src/didactopus/orm.py", + "stripped_member": "src/didactopus/orm.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orm.py", + "action": "overwrite", + "sha256": "45eda62509c8d251ac92f799d0f6bad8e47cd87e05d8049dd40adf93917991b8", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "overwrite", + "sha256": "5bfbc5490259842a73aeb9a3f4182ed948c865341ed532b19c1593551dc020d7", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "src/didactopus/auth.py", + "stripped_member": "src/didactopus/auth.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/auth.py", + "action": "overwrite", + "sha256": "e3abaf693b4cb48b1e04923a92c3652bb6d01efd568e35c759de41b7d9de3dcb", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "src/didactopus/engine.py", + "stripped_member": "src/didactopus/engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/engine.py", + "action": "overwrite", + "sha256": "16dadf1b4f917b1b2c67edfbf8bf494c6753c3d6269778832daeea8795a4022d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "src/didactopus/repository.py", + "stripped_member": "src/didactopus/repository.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/repository.py", + "action": "overwrite", + "sha256": "29e584cb28e73e9ada5cc4d280511406815d1abe425fb7fc6465ceddd328de72", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "src/didactopus/worker.py", + "stripped_member": "src/didactopus/worker.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/worker.py", + "action": "overwrite", + "sha256": "4f9f7824af48cda94b7e05e94c9d4719dae0f2954b60664c86d86d5f57bea537", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "src/didactopus/seed.py", + "stripped_member": "src/didactopus/seed.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/seed.py", + "action": "overwrite", + "sha256": "4aa713a6e0a51df9b03169bb97cb696711ce320164c9049b938ddb5d66838c5d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "overwrite", + "sha256": "42cc301536ae1ce370332ab26b09e6f5a3dbcb499edf684d640e70e80d3ebd16", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/140-didactopus-admin-learner-ui-workflows.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_131923__145-didactopus-agent-audit-and-key-rotation-layer__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "f70b8b06d8e738a7df561f5eeca116067051a48abbb4d99575f09a413dee7634", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "990c48fcba62c3eb8b16a32d913c7e4a8d4ba0d4748bca1b88547b69ed88a743", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "3757303551ab2e91bd2646ea220814d6d539f97e28639a24a0133b9a74c0ba26", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "tests/test_scaffold_files.py", + "stripped_member": "tests/test_scaffold_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_scaffold_files.py", + "action": "overwrite", + "sha256": "422ed2af486b957f852c599c8c779d7838711f1d9f0ad9c1e03a5d223544753a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "53d2eee84c6871b3c67ca385d4a4fcc84f4f6c40c89fdc0db52e5219f94bcf4c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "webui/src/api.js", + "stripped_member": "webui/src/api.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/api.js", + "action": "overwrite", + "sha256": "dde6c92c197f055aeda9cc1297debfb4b0c4d29d5432cd6a0f11c54b3694ca42", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "webui/src/authStore.js", + "stripped_member": "webui/src/authStore.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/authStore.js", + "action": "overwrite", + "sha256": "d26a0e35b95996613ef24574fdfc8d97bfd2df9d8ef9c1cff5833461d162c4a0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "89e094b1fa94d553639e64b9aae072eaa4304ffa6342f0e620b4c1e14fc288a5", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "0ffad9aa72eadeace0ada6f4d186a5a7c179dcbe344a7c8dce61f6b7822d9001", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "240dc5d84ff9c1ba44e9a6328927f240d44e9543f757831488ee5bd32e03be52", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/db.py", + "stripped_member": "src/didactopus/db.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/db.py", + "action": "overwrite", + "sha256": "5773454c709c7288aed68dbd73e3134deececb328214ff5cf649a51fd0ea7790", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/orm.py", + "stripped_member": "src/didactopus/orm.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orm.py", + "action": "overwrite", + "sha256": "212ba1b78b8cc89ae98dc4e87e1e939c757d90e66f2c8eba20f0215525a50397", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "overwrite", + "sha256": "49d6622d7a8d54ee20da35e13af68ff916a74bc2a5e50afc082684a167ed43a6", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/auth.py", + "stripped_member": "src/didactopus/auth.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/auth.py", + "action": "overwrite", + "sha256": "8b735b8e0b01a11e86f942d1a10449a62b29b70a0f2eebd94fd836040c2a2d5c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/engine.py", + "stripped_member": "src/didactopus/engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/engine.py", + "action": "overwrite", + "sha256": "16dadf1b4f917b1b2c67edfbf8bf494c6753c3d6269778832daeea8795a4022d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/repository.py", + "stripped_member": "src/didactopus/repository.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/repository.py", + "action": "overwrite", + "sha256": "8003efba24554451f46739ab60af0a39991419e8bd2e1b2909efb42f5228ab93", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/worker.py", + "stripped_member": "src/didactopus/worker.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/worker.py", + "action": "overwrite", + "sha256": "06d860240032eec72bcc4e8af927564583a45a2b246fe679093c43f19f1caf6b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/seed.py", + "stripped_member": "src/didactopus/seed.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/seed.py", + "action": "overwrite", + "sha256": "b139db9f4a028b59e1dd1c3cfad978b59604030bc13d64a54e211fa5e0f4f4cf", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "overwrite", + "sha256": "05c5fd813d3dda6708eb882aa56ec713b82a65548ee7e10a20746ab7d953b36d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/145-didactopus-agent-audit-and-key-rotation-layer.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_131926__150-didactopus-agent-service-account-layer__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "ed3405034f233d4319d9a34b3cc2ecca587c481362afcd585b9c656f506da798", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "43d64fcae3358bbbc8187b3a544ac377a8bfaf99563cf56abd20da9996350087", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "5a2dcf937e294f3f5dfa3296013bbacd1ba6d5467cdeda883d23cadd2f919c2e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "tests/test_scaffold_files.py", + "stripped_member": "tests/test_scaffold_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_scaffold_files.py", + "action": "overwrite", + "sha256": "422ed2af486b957f852c599c8c779d7838711f1d9f0ad9c1e03a5d223544753a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "53d2eee84c6871b3c67ca385d4a4fcc84f4f6c40c89fdc0db52e5219f94bcf4c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "webui/src/api.js", + "stripped_member": "webui/src/api.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/api.js", + "action": "overwrite", + "sha256": "e017e862dd50a283101936d60fce60b9d6712b6baefc1f866dd9c2799fcb4a58", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "webui/src/authStore.js", + "stripped_member": "webui/src/authStore.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/authStore.js", + "action": "overwrite", + "sha256": "d26a0e35b95996613ef24574fdfc8d97bfd2df9d8ef9c1cff5833461d162c4a0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "46f79ae8241faf70c0e1ab5dbae95c3707b58358daa4daa41fc0fc70765d51a5", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "ce74cc7969523ab0f65930c5eb5927e97c84860f7e94866668f6dab0953bd839", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "240dc5d84ff9c1ba44e9a6328927f240d44e9543f757831488ee5bd32e03be52", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "src/didactopus/db.py", + "stripped_member": "src/didactopus/db.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/db.py", + "action": "overwrite", + "sha256": "5773454c709c7288aed68dbd73e3134deececb328214ff5cf649a51fd0ea7790", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "src/didactopus/orm.py", + "stripped_member": "src/didactopus/orm.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orm.py", + "action": "overwrite", + "sha256": "5bdd32b7158090470b5ac352ca62bd5a701c13c9d72fa6e49bbca5a36d401611", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "overwrite", + "sha256": "a48f43b1572814ea405502084b0cf138408bd02c816bfdf561b2c1fc9a86d14e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "src/didactopus/auth.py", + "stripped_member": "src/didactopus/auth.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/auth.py", + "action": "overwrite", + "sha256": "8b735b8e0b01a11e86f942d1a10449a62b29b70a0f2eebd94fd836040c2a2d5c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "src/didactopus/engine.py", + "stripped_member": "src/didactopus/engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/engine.py", + "action": "overwrite", + "sha256": "16dadf1b4f917b1b2c67edfbf8bf494c6753c3d6269778832daeea8795a4022d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "src/didactopus/repository.py", + "stripped_member": "src/didactopus/repository.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/repository.py", + "action": "overwrite", + "sha256": "6ddc5a5f60b6c7ccd8b858ec90e3cbba3577338a7d17a73f6cc72402de2af4a5", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "src/didactopus/worker.py", + "stripped_member": "src/didactopus/worker.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/worker.py", + "action": "overwrite", + "sha256": "06d860240032eec72bcc4e8af927564583a45a2b246fe679093c43f19f1caf6b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "src/didactopus/seed.py", + "stripped_member": "src/didactopus/seed.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/seed.py", + "action": "overwrite", + "sha256": "b139db9f4a028b59e1dd1c3cfad978b59604030bc13d64a54e211fa5e0f4f4cf", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "overwrite", + "sha256": "4d196227ba0541368625b318c363581df3c17bdacb4617a12b118551f44a6eb6", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/150-didactopus-agent-service-account-layer.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_131930__155-didactopus-animated-concept-graph-layer__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "f70b8b06d8e738a7df561f5eeca116067051a48abbb4d99575f09a413dee7634", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "bb4397c9936e6f4c0ddcd669ec81b5d8a9f6e39a1f7e9cdfa3c227f8fa46c0a6", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "4143b232b1b771e5e1caeeef4ab9a651675a839f3c3da47b7d0d2bb6fb0aaca3", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "member": "tests/test_scaffold_files.py", + "stripped_member": "tests/test_scaffold_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_scaffold_files.py", + "action": "overwrite", + "sha256": "422ed2af486b957f852c599c8c779d7838711f1d9f0ad9c1e03a5d223544753a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "53d2eee84c6871b3c67ca385d4a4fcc84f4f6c40c89fdc0db52e5219f94bcf4c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "member": "webui/src/api.js", + "stripped_member": "webui/src/api.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/api.js", + "action": "overwrite", + "sha256": "5524317af807070d0431cc6266994f77514d21ef8e3dfd548c9da8a666538983", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "member": "webui/src/authStore.js", + "stripped_member": "webui/src/authStore.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/authStore.js", + "action": "overwrite", + "sha256": "d26a0e35b95996613ef24574fdfc8d97bfd2df9d8ef9c1cff5833461d162c4a0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "5bda6fe8bec4c461cf872e4f2a8717ce3e2c6e5f324be122dc1338732d5b9da6", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "cd2cd444dddcf53daee974547e4b7a9e4c819f1ec6201666d23fa405a71a7bc7", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "65ed36aba6467cdb9ba343460c2e59d10dd190c86f9d8c4450cdb5cf616c502f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "member": "src/didactopus/db.py", + "stripped_member": "src/didactopus/db.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/db.py", + "action": "overwrite", + "sha256": "5773454c709c7288aed68dbd73e3134deececb328214ff5cf649a51fd0ea7790", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "member": "src/didactopus/orm.py", + "stripped_member": "src/didactopus/orm.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orm.py", + "action": "overwrite", + "sha256": "cc9a5857a38e4df2a5b1fe64517a18da39c6f943f96ec2dfc8dbb5957027632c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "overwrite", + "sha256": "0e1dc8fd7cf1ac8093ed80742f6c24f3e7fbe8428bcfa707d252dd0b883e6880", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "member": "src/didactopus/auth.py", + "stripped_member": "src/didactopus/auth.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/auth.py", + "action": "overwrite", + "sha256": "554a347973d3beec8511c99074a583d9679c7d06d84b5573df4220c75830bd23", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "member": "src/didactopus/engine.py", + "stripped_member": "src/didactopus/engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/engine.py", + "action": "overwrite", + "sha256": "048d2c43be255bc3a19ca816eac5dbefdac391b46d0fcf4220f967ebde59c9c7", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "member": "src/didactopus/repository.py", + "stripped_member": "src/didactopus/repository.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/repository.py", + "action": "overwrite", + "sha256": "2d465c15aecd6d5279a289edd81f7e9e3d47546adaf8f23cfc8526e5ff29a8e3", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "member": "src/didactopus/seed.py", + "stripped_member": "src/didactopus/seed.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/seed.py", + "action": "overwrite", + "sha256": "952803ca160d5bdb3437968c981dfbf906a0d2002a03e498b45fe54a7be73d60", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "overwrite", + "sha256": "f2623cc2a7cd6f15d258d4badd56ea11f7775cb850585086f2fbd3e63d97dd42", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/155-didactopus-animated-concept-graph-layer.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_131933__160-didactopus-artifact-registry-layer__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "b87db7a0d89a8df478bcb3b2105fab382d0eda49bdb9e7b52c043b8674025236", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "c45230a21c768063d7b6f2a6a9ff0b8354e9520859ec8c098eb16935fd2bde9b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "5b783ea38ef8bc1b34b58bb81cb50828cbc6b482d0dad5f3c601e9ae8a83dad4", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "tests/test_scaffold_files.py", + "stripped_member": "tests/test_scaffold_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_scaffold_files.py", + "action": "overwrite", + "sha256": "e71ada3084c3163409be7838e79f8b150919043714407860abdf6073e1d0acb0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "53d2eee84c6871b3c67ca385d4a4fcc84f4f6c40c89fdc0db52e5219f94bcf4c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "webui/src/api.js", + "stripped_member": "webui/src/api.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/api.js", + "action": "overwrite", + "sha256": "86bc1f9f3495836820518579a277b9ae2a8e7f5392e49ee5db648158749b4a3d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "webui/src/authStore.js", + "stripped_member": "webui/src/authStore.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/authStore.js", + "action": "overwrite", + "sha256": "d26a0e35b95996613ef24574fdfc8d97bfd2df9d8ef9c1cff5833461d162c4a0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "f1ff613b73b0a9b36d27fcbe46ca2c2a7e77e580f91da993cfc84ce69ff939d9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "527599a7f2dad492b776ad82ed1cfb54e26d9045e97c07d9f47265a19885bbe5", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "65ed36aba6467cdb9ba343460c2e59d10dd190c86f9d8c4450cdb5cf616c502f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "src/didactopus/db.py", + "stripped_member": "src/didactopus/db.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/db.py", + "action": "overwrite", + "sha256": "5773454c709c7288aed68dbd73e3134deececb328214ff5cf649a51fd0ea7790", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "src/didactopus/orm.py", + "stripped_member": "src/didactopus/orm.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orm.py", + "action": "overwrite", + "sha256": "e926391bc20583f88c92eb4fb880ac0ca14a0ece0a026434b49715fd8b0388b8", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "overwrite", + "sha256": "f906ab1279e1e82c6c10af452fbfb022cae3f604e71d7b03b6652a32dc1919d6", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "src/didactopus/auth.py", + "stripped_member": "src/didactopus/auth.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/auth.py", + "action": "overwrite", + "sha256": "554a347973d3beec8511c99074a583d9679c7d06d84b5573df4220c75830bd23", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "src/didactopus/engine.py", + "stripped_member": "src/didactopus/engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/engine.py", + "action": "overwrite", + "sha256": "9e4fb26876c397ad14c9959f77f6790968bcbb773c7924c21e62dafd3c326908", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "src/didactopus/export_svg.py", + "stripped_member": "src/didactopus/export_svg.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/export_svg.py", + "action": "create", + "sha256": "66ac201888b73537cdf2ce3f4c6bb912e67f49679d82339378418575be0c3c27", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "src/didactopus/render_bundle.py", + "stripped_member": "src/didactopus/render_bundle.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/render_bundle.py", + "action": "create", + "sha256": "d448e20bd0829a12b1e35b0c2729396db5ddf6ab6e12aefdc322638dcf3793ad", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "src/didactopus/worker.py", + "stripped_member": "src/didactopus/worker.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/worker.py", + "action": "overwrite", + "sha256": "8e4d56cafdba62c1a2f829eb57e396da34349bb81f51d8f157de6aff17b28af9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "src/didactopus/seed.py", + "stripped_member": "src/didactopus/seed.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/seed.py", + "action": "overwrite", + "sha256": "66122cf457e3ae06f12144f3113dc9f700ab61a9fa38daf1b05754f087004a18", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "overwrite", + "sha256": "5f6f8c5e3b2781f50de9ab920cb373a5d1143aaf07a119afd17c25b6581de97b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/160-didactopus-artifact-registry-layer.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/165-didactopus-attribution-provenance-update.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132003__165-didactopus-attribution-provenance-update__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/165-didactopus-attribution-provenance-update.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "b905ced19cfad98c676730d72659a2c083076548f2fca8faa0d5ae988acfe649", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/165-didactopus-attribution-provenance-update.zip", + "member": "docs/attribution-and-provenance.md", + "stripped_member": "docs/attribution-and-provenance.md", + "dest": "/home/netuser/dev/Didactopus/docs/attribution-and-provenance.md", + "action": "create", + "sha256": "bea1519fc9de1d76e61dcee0f9520eeccca10e79eea1aeb6f8604fc8a346d02d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/165-didactopus-attribution-provenance-update.zip", + "member": "docs/mit-ocw-notes.md", + "stripped_member": "docs/mit-ocw-notes.md", + "dest": "/home/netuser/dev/Didactopus/docs/mit-ocw-notes.md", + "action": "create", + "sha256": "ce5ef2bbace3bece34479f11d3580f937edf47dcd301fa73fec1f55cd0922cc3", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/165-didactopus-attribution-provenance-update.zip", + "member": "samples/sources.yaml", + "stripped_member": "samples/sources.yaml", + "dest": "/home/netuser/dev/Didactopus/samples/sources.yaml", + "action": "create", + "sha256": "b2c03987c8d31a3c681c6ad7c3301178bf388416877af68484b79b4113fa2d94", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/165-didactopus-attribution-provenance-update.zip", + "member": "samples/ATTRIBUTION.md", + "stripped_member": "samples/ATTRIBUTION.md", + "dest": "/home/netuser/dev/Didactopus/samples/ATTRIBUTION.md", + "action": "create", + "sha256": "e9a62a7405bb11fd34bcfdd5eb0ee674ff14fcaa103ffe15e38d8c0b7c5dea6e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/165-didactopus-attribution-provenance-update.zip", + "member": "samples/provenance_manifest.json", + "stripped_member": "samples/provenance_manifest.json", + "dest": "/home/netuser/dev/Didactopus/samples/provenance_manifest.json", + "action": "create", + "sha256": "24610c4d30414a4421e44462af0ab642d683c9c4f53849f183173badb9d77ffc", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/165-didactopus-attribution-provenance-update.zip", + "member": "tests/test_attribution_qa.py", + "stripped_member": "tests/test_attribution_qa.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_attribution_qa.py", + "action": "create", + "sha256": "555f0cd93a7756f14ed5e04a7b46415a7ce3958e56ad37d79a93df79214c1d56", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/165-didactopus-attribution-provenance-update.zip", + "member": "tests/test_attribution_builder.py", + "stripped_member": "tests/test_attribution_builder.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_attribution_builder.py", + "action": "create", + "sha256": "e5c4835be2e3fee8a815e919301c039f2c15e10f870c93f721697318d243ca1c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/165-didactopus-attribution-provenance-update.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/165-didactopus-attribution-provenance-update.zip", + "member": "src/didactopus/source_models.py", + "stripped_member": "src/didactopus/source_models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/source_models.py", + "action": "create", + "sha256": "3f907061575703268eeefed47391649c8cfa08dc92cc5f63bbaf95b50dbfaff4", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/165-didactopus-attribution-provenance-update.zip", + "member": "src/didactopus/provenance.py", + "stripped_member": "src/didactopus/provenance.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/provenance.py", + "action": "create", + "sha256": "0a9900e310daa3ee1670e6ea98f343f5e94c3d678751a0528a5100d3b069f382", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/165-didactopus-attribution-provenance-update.zip", + "member": "src/didactopus/attribution_builder.py", + "stripped_member": "src/didactopus/attribution_builder.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/attribution_builder.py", + "action": "create", + "sha256": "c9ee5f5052465a939a62baf2f25be866268f9e050f797a47c0abfa04e462f83a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/165-didactopus-attribution-provenance-update.zip", + "member": "src/didactopus/attribution_qa.py", + "stripped_member": "src/didactopus/attribution_qa.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/attribution_qa.py", + "action": "create", + "sha256": "66fd23b041a0d3d85f2775c2a5051fc0e8cfef8c22e870bffd5957914cf63aec", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/165-didactopus-attribution-provenance-update.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132010__170-didactopus-auth-db-async-evaluator-prototype__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "9afe58b63f2fe1b014ed018ddbdc3cec6358785dced4c3728897861d0ee07e00", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "13fc37e1d150615bd4c61e13bcbd4f32e6ca49893affd8d22cf5bd6b9686f846", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "383593847c1de2c702786512da1ba85e9f6ac086760cd817e52ee64a80bea1a7", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "tests/test_backend_files.py", + "stripped_member": "tests/test_backend_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_backend_files.py", + "action": "create", + "sha256": "ada92c7b8e81a1141a52b424cbb35bf3211aa00058600d4fa44a8796f5b22a02", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "tests/test_frontend_files.py", + "stripped_member": "tests/test_frontend_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_frontend_files.py", + "action": "create", + "sha256": "53599da7b205c24662df70122c430df43ac82eb770a7436412200b625eb0971e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "73c55856ad1e60177fa61d52a6841b2166b7b3d0a0276b1449cdd33e2bb6e421", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "webui/src/api.js", + "stripped_member": "webui/src/api.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/api.js", + "action": "overwrite", + "sha256": "107c5615de4624074746274212e58f1b3237292f7c2b116d52661e4fa89735e0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "webui/src/localEngine.js", + "stripped_member": "webui/src/localEngine.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/localEngine.js", + "action": "create", + "sha256": "ae2f6bb5adaea5c5e310c930cf6ddcdc4ba7e88f18acb3b6676a07381220cec0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "2f391718468db0f7103ea57b74f5aaf4722546c6a1f775c5d4a9a93dc1541eb1", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "59499d8f2e2855d78397cb25c142f1ce6dc90652a884b3b98fc0c38ebc4cbced", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "4f62a8a2a4e74457cb9af7e9ccf2196d2227395eb7deb446a7b9b6f44371d6f4", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "src/didactopus/db.py", + "stripped_member": "src/didactopus/db.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/db.py", + "action": "overwrite", + "sha256": "64a6b5dc3ea26a665963dbe9f3184b895c86de79dba50b5987bc0d30253a2a2d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "src/didactopus/orm.py", + "stripped_member": "src/didactopus/orm.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orm.py", + "action": "overwrite", + "sha256": "df7909b6278bb54830dd69bf3654d7121f566a309751dbd455b64115ec878927", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "overwrite", + "sha256": "e2e5cbeb781fe0a9f2d5a055b75697fa39fd562374ca984e7768242f315c84cc", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "src/didactopus/auth.py", + "stripped_member": "src/didactopus/auth.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/auth.py", + "action": "overwrite", + "sha256": "4794b9133c2178f4d19744cf9f9331e0753f152e9cce02ff7b53c3814b96c4da", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "src/didactopus/engine.py", + "stripped_member": "src/didactopus/engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/engine.py", + "action": "overwrite", + "sha256": "86bf54755296b4b92f619aa310944f567e711c78dba101bdd56de32933b27428", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "src/didactopus/repository.py", + "stripped_member": "src/didactopus/repository.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/repository.py", + "action": "overwrite", + "sha256": "609feed6899f550d9bcc482618a5007eb4073c6a03b38eb1d1188ac8ec39f8fe", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "src/didactopus/seed.py", + "stripped_member": "src/didactopus/seed.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/seed.py", + "action": "overwrite", + "sha256": "5d7653b863156e657c2184f5ecccce6fb706ddb5fe1747da04bd45cbbe10d3bd", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "overwrite", + "sha256": "ead5e4567d0d060a9b017b28224fc0c4692c73803e607e5e02d2aa49bc110d42", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/170-didactopus-auth-db-async-evaluator-prototype.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/175-didactopus-backend-api-prototype.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132013__175-didactopus-backend-api-prototype__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/175-didactopus-backend-api-prototype.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "ac15b444a6cc25467fe399ae17cfa8d5a3dcfa8408be858c2bd38530161f84fc", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/175-didactopus-backend-api-prototype.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "51b58825060b7a549fb38db562e150927561052fd068acd2e2929e3a1974f33f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/175-didactopus-backend-api-prototype.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "6f362053115b2510b30240586cbf533dac04f687acef5303a68de75d66c1b14e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/175-didactopus-backend-api-prototype.zip", + "member": "tests/test_api_scaffold.py", + "stripped_member": "tests/test_api_scaffold.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_api_scaffold.py", + "action": "create", + "sha256": "0413ec7afef89d58e321bcee510af66a1ac800fe5962fa8ec788579a83d8215f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/175-didactopus-backend-api-prototype.zip", + "member": "tests/test_pack_export.py", + "stripped_member": "tests/test_pack_export.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_pack_export.py", + "action": "create", + "sha256": "03b8b8b829b7df5af3177c0f7a3e3dd946e6c8afffada765373e69a6f3fa9333", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/175-didactopus-backend-api-prototype.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "73c55856ad1e60177fa61d52a6841b2166b7b3d0a0276b1449cdd33e2bb6e421", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/175-didactopus-backend-api-prototype.zip", + "member": "webui/src/api.js", + "stripped_member": "webui/src/api.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/api.js", + "action": "overwrite", + "sha256": "4e6fc9871d441d1477c8b0c1255d5064b0e5bfdb2a7a90f9a62e401379e843d9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/175-didactopus-backend-api-prototype.zip", + "member": "webui/src/localEngine.js", + "stripped_member": "webui/src/localEngine.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/localEngine.js", + "action": "overwrite", + "sha256": "ec3672aee35a4632c27a7be26208f4b454067e5cc9d88ff6119cade85c49f61a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/175-didactopus-backend-api-prototype.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "4203338b5083bddf62498671579c1b3e4e37fbecddc10da9c28eefaa1310973b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/175-didactopus-backend-api-prototype.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "aba7965dd553891f67eb685a57e7015b1cb6bbc5f1d945c58312bd10d35488f5", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/175-didactopus-backend-api-prototype.zip", + "member": "data/packs/bayes-pack.json", + "stripped_member": "data/packs/bayes-pack.json", + "dest": "/home/netuser/dev/Didactopus/data/packs/bayes-pack.json", + "action": "create", + "sha256": "cdcedf1e3e9444e634f5ad2ed1c64dffefd70b7428603ecd58aa2242cf4609e2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/175-didactopus-backend-api-prototype.zip", + "member": "data/packs/stats-pack.json", + "stripped_member": "data/packs/stats-pack.json", + "dest": "/home/netuser/dev/Didactopus/data/packs/stats-pack.json", + "action": "create", + "sha256": "7e387aee07646b47fc5de857dacca0f3caef74e48cf7c6d57da0808c903e9209", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/175-didactopus-backend-api-prototype.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/175-didactopus-backend-api-prototype.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "overwrite", + "sha256": "d29742514731525cd9d9a6711ed9d1419ad79c8392ecad00c8664a3534ba5d38", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/175-didactopus-backend-api-prototype.zip", + "member": "src/didactopus/engine.py", + "stripped_member": "src/didactopus/engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/engine.py", + "action": "overwrite", + "sha256": "86bf54755296b4b92f619aa310944f567e711c78dba101bdd56de32933b27428", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/175-didactopus-backend-api-prototype.zip", + "member": "src/didactopus/storage.py", + "stripped_member": "src/didactopus/storage.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/storage.py", + "action": "create", + "sha256": "9a6958e7098f3e2df9638f678c38293e4e51c992a978debf3f7ddc76d018f323", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/175-didactopus-backend-api-prototype.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "overwrite", + "sha256": "584b4f4fc3c7ec7f035edd882146f25e786f4647f30172da31f5cfe98346af95", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/175-didactopus-backend-api-prototype.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132017__180-didactopus-contribution-management-layer__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "ed3405034f233d4319d9a34b3cc2ecca587c481362afcd585b9c656f506da798", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "68e4541d2c5760b890fef5452de393ac809e29c3f5641eea981709adb27dd877", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "8e56aa77bdb32f39afc2361a1f55a52e8d7fdb38e7426aac0e4fcee1e4faa4cc", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "tests/test_scaffold_files.py", + "stripped_member": "tests/test_scaffold_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_scaffold_files.py", + "action": "overwrite", + "sha256": "422ed2af486b957f852c599c8c779d7838711f1d9f0ad9c1e03a5d223544753a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "53d2eee84c6871b3c67ca385d4a4fcc84f4f6c40c89fdc0db52e5219f94bcf4c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "webui/src/api.js", + "stripped_member": "webui/src/api.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/api.js", + "action": "overwrite", + "sha256": "cb576ba9645cd0de53a79a8b3356be9a0308102124ab79a6b57446c10bd62fc7", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "webui/src/authStore.js", + "stripped_member": "webui/src/authStore.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/authStore.js", + "action": "overwrite", + "sha256": "d26a0e35b95996613ef24574fdfc8d97bfd2df9d8ef9c1cff5833461d162c4a0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "d7f6dc6a1e173e05a8a8e1c97b4f62e6872318f2cf2ed7ab700a280591815034", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "e84d7a2540786f27e2dd28cd9a759ecf222c9e16a5016fd110eeb4acd48d1437", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "23d092311fbefc5999518a7b92718e7435835c70839e663171b646bdba8006cd", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "src/didactopus/db.py", + "stripped_member": "src/didactopus/db.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/db.py", + "action": "overwrite", + "sha256": "5773454c709c7288aed68dbd73e3134deececb328214ff5cf649a51fd0ea7790", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "src/didactopus/orm.py", + "stripped_member": "src/didactopus/orm.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orm.py", + "action": "overwrite", + "sha256": "cf93c901c77874aa73439ba348a27af6eecf93619ebd703f84d81b701a906741", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "overwrite", + "sha256": "bf95e71d9827d3ac1c1a8f9a911ac7a504a92312e2e9f8e1f10f709bbd4564e4", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "src/didactopus/auth.py", + "stripped_member": "src/didactopus/auth.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/auth.py", + "action": "overwrite", + "sha256": "554a347973d3beec8511c99074a583d9679c7d06d84b5573df4220c75830bd23", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "src/didactopus/engine.py", + "stripped_member": "src/didactopus/engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/engine.py", + "action": "overwrite", + "sha256": "16dadf1b4f917b1b2c67edfbf8bf494c6753c3d6269778832daeea8795a4022d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "src/didactopus/repository.py", + "stripped_member": "src/didactopus/repository.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/repository.py", + "action": "overwrite", + "sha256": "10a98152e7bc1c4d4949d882cdcb5b92b585f55de2c8dd835bfb79145641e85a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "src/didactopus/worker.py", + "stripped_member": "src/didactopus/worker.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/worker.py", + "action": "overwrite", + "sha256": "4dc2ad94224943d12846b513aec761361f71340b2731a45957e7c38f0f8ac93e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "src/didactopus/seed.py", + "stripped_member": "src/didactopus/seed.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/seed.py", + "action": "overwrite", + "sha256": "55df620e01e2c98bfdddbe27182651e225f862ab2f0c0bc90e0ba273a38acb8f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "overwrite", + "sha256": "26cd1cf0824650446b23a42a140949c5e0f3b08ef38f6bfd7df6173b82efd9c0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/180-didactopus-contribution-management-layer.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/185-didactopus-course-compliance-ui-prototype.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132021__185-didactopus-course-compliance-ui-prototype__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/185-didactopus-course-compliance-ui-prototype.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "251111b6207f6c48cd672d73f57faae464fde3b31ec006c8f80061d9f105ddf7", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/185-didactopus-course-compliance-ui-prototype.zip", + "member": "docs/license-compliance.md", + "stripped_member": "docs/license-compliance.md", + "dest": "/home/netuser/dev/Didactopus/docs/license-compliance.md", + "action": "create", + "sha256": "0a834da991612ab49ee9511e15f6c39b0f5bc1e611c7364b4bb65d1f8b9f7280", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/185-didactopus-course-compliance-ui-prototype.zip", + "member": "samples/sources.yaml", + "stripped_member": "samples/sources.yaml", + "dest": "/home/netuser/dev/Didactopus/samples/sources.yaml", + "action": "overwrite", + "sha256": "705fc138d4fa1bccbca40fe10241a6deb8df4504c1709bbca74c7dd24eee822c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/185-didactopus-course-compliance-ui-prototype.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "63c284c403d9b2db6e9d00e8434747197183d8fb5838c8b83a321eb014b58709", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/185-didactopus-course-compliance-ui-prototype.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "76e875c5e8d6fc3285ed7571e21033e52660150577677603932e56d0ec7a8740", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/185-didactopus-course-compliance-ui-prototype.zip", + "member": "tests/test_compliance.py", + "stripped_member": "tests/test_compliance.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_compliance.py", + "action": "create", + "sha256": "3e12a5ffaee722a16f4facfb9ca2ca2350b7a956e776fda556b622075b4cb924", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/185-didactopus-course-compliance-ui-prototype.zip", + "member": "tests/test_ui_scaffold.py", + "stripped_member": "tests/test_ui_scaffold.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_ui_scaffold.py", + "action": "overwrite", + "sha256": "635ef9b562f1f2ca63326c9d3137b2c4f4f8ac196716e78574be59df1fc8c347", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/185-didactopus-course-compliance-ui-prototype.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "73c55856ad1e60177fa61d52a6841b2166b7b3d0a0276b1449cdd33e2bb6e421", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/185-didactopus-course-compliance-ui-prototype.zip", + "member": "webui/src/sampleData.js", + "stripped_member": "webui/src/sampleData.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/sampleData.js", + "action": "create", + "sha256": "ed5e0938b7dd5c9d4f1b393fa6412cd3550860c1a4c55b94c99e4761a0d624f9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/185-didactopus-course-compliance-ui-prototype.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "d001f8ebe46f440ca5e1a31a9b814faf2b50fab1735c95bf54f318f433818778", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/185-didactopus-course-compliance-ui-prototype.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "556ee518ddf4c7d65dcad98b2ce84b49110366b7dae903301d397a64111d847c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/185-didactopus-course-compliance-ui-prototype.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/185-didactopus-course-compliance-ui-prototype.zip", + "member": "src/didactopus/compliance_models.py", + "stripped_member": "src/didactopus/compliance_models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/compliance_models.py", + "action": "create", + "sha256": "1dd7ddd1bfa14c251424c5f048d4ecb2b7610c5af03f2314307e8e2e4ccd1fb5", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/185-didactopus-course-compliance-ui-prototype.zip", + "member": "src/didactopus/course_ingestion_compliance.py", + "stripped_member": "src/didactopus/course_ingestion_compliance.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/course_ingestion_compliance.py", + "action": "create", + "sha256": "00be5a5569b9178e8736c81a88619343862fcc13ca316a27553b6bfc4571b617", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/185-didactopus-course-compliance-ui-prototype.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132024__190-didactopus-deployment-policy-and-agent-hooks__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "ed3405034f233d4319d9a34b3cc2ecca587c481362afcd585b9c656f506da798", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "470951d3ef1ff33b9056808658421bf07028d7ebb4a346793bdfd5b13e8fb581", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "df1f437908f6929bb488bf1607ddbe214835fb13ecfbfb74bed28060accb9b35", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "tests/test_scaffold_files.py", + "stripped_member": "tests/test_scaffold_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_scaffold_files.py", + "action": "overwrite", + "sha256": "422ed2af486b957f852c599c8c779d7838711f1d9f0ad9c1e03a5d223544753a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "53d2eee84c6871b3c67ca385d4a4fcc84f4f6c40c89fdc0db52e5219f94bcf4c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "webui/src/api.js", + "stripped_member": "webui/src/api.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/api.js", + "action": "overwrite", + "sha256": "73a7a728a629496b29c79ac5250570c8f61b7ceadc359a3fd9af605929c279a9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "webui/src/authStore.js", + "stripped_member": "webui/src/authStore.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/authStore.js", + "action": "overwrite", + "sha256": "d26a0e35b95996613ef24574fdfc8d97bfd2df9d8ef9c1cff5833461d162c4a0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "6f06a532eb22ab7dd8379cfa26d5d22347198987cf2b30f450e7bfab73b50b62", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "68344996e724cbe4b8231863e29365f09713ff6eb58243b77ee3693d03ee7a7e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "f60b290c3b000dbe417e6912583774b27ce28439540d31dc2bd165f0304470e0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "src/didactopus/db.py", + "stripped_member": "src/didactopus/db.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/db.py", + "action": "overwrite", + "sha256": "5773454c709c7288aed68dbd73e3134deececb328214ff5cf649a51fd0ea7790", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "src/didactopus/orm.py", + "stripped_member": "src/didactopus/orm.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orm.py", + "action": "overwrite", + "sha256": "ce7c912db1d9a296b001227df65974717026ee17ce3607e522f6703960db0217", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "overwrite", + "sha256": "125d79ec587a89b90868f0e55cee57ff97a95cdaa3f9288a9189f63e9833d126", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "src/didactopus/auth.py", + "stripped_member": "src/didactopus/auth.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/auth.py", + "action": "overwrite", + "sha256": "554a347973d3beec8511c99074a583d9679c7d06d84b5573df4220c75830bd23", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "src/didactopus/engine.py", + "stripped_member": "src/didactopus/engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/engine.py", + "action": "overwrite", + "sha256": "16dadf1b4f917b1b2c67edfbf8bf494c6753c3d6269778832daeea8795a4022d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "src/didactopus/repository.py", + "stripped_member": "src/didactopus/repository.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/repository.py", + "action": "overwrite", + "sha256": "064443e93b525d89c00c662a0830e9e1b256462f7be5fa880fd5c718a6df808a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "src/didactopus/worker.py", + "stripped_member": "src/didactopus/worker.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/worker.py", + "action": "overwrite", + "sha256": "4dc2ad94224943d12846b513aec761361f71340b2731a45957e7c38f0f8ac93e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "src/didactopus/seed.py", + "stripped_member": "src/didactopus/seed.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/seed.py", + "action": "overwrite", + "sha256": "201515d9646e8b0ee01e435af3d13cfbca633b281da162aa759281cd7ff35b8a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "overwrite", + "sha256": "0d82eb8f37deb58470e239ca55fb4fb27eaaa1dea1ed2c04f7dd103b303bd46f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/190-didactopus-deployment-policy-and-agent-hooks.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132027__195-didactopus-dual-lane-policy-layer__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "ed3405034f233d4319d9a34b3cc2ecca587c481362afcd585b9c656f506da798", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "c584895bd4df54954f00c2ea838c43e66ba4b169ede76c93bcc449554ab2fb84", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "e33a38eb96a51e7d5ea359684a44b1c95ba251a13145a170efcd5088ebe305a8", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "tests/test_scaffold_files.py", + "stripped_member": "tests/test_scaffold_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_scaffold_files.py", + "action": "overwrite", + "sha256": "422ed2af486b957f852c599c8c779d7838711f1d9f0ad9c1e03a5d223544753a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "53d2eee84c6871b3c67ca385d4a4fcc84f4f6c40c89fdc0db52e5219f94bcf4c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "webui/src/api.js", + "stripped_member": "webui/src/api.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/api.js", + "action": "overwrite", + "sha256": "08360b7e5dbb6d05cc73d0a2935fd3dcb0545e5675c0c6ce0c3560b5d7891b1c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "webui/src/authStore.js", + "stripped_member": "webui/src/authStore.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/authStore.js", + "action": "overwrite", + "sha256": "d26a0e35b95996613ef24574fdfc8d97bfd2df9d8ef9c1cff5833461d162c4a0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "9a24e92c196b8426077560c1245d3132f580cfaa712f568f43075b6dd73772e2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "e84d7a2540786f27e2dd28cd9a759ecf222c9e16a5016fd110eeb4acd48d1437", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "23d092311fbefc5999518a7b92718e7435835c70839e663171b646bdba8006cd", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "src/didactopus/db.py", + "stripped_member": "src/didactopus/db.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/db.py", + "action": "overwrite", + "sha256": "5773454c709c7288aed68dbd73e3134deececb328214ff5cf649a51fd0ea7790", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "src/didactopus/orm.py", + "stripped_member": "src/didactopus/orm.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orm.py", + "action": "overwrite", + "sha256": "3b247c2dfc9eb4fecabaf31f36367ff1f186fb5e38ec291bdb1a10a04453e73d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "overwrite", + "sha256": "a35ce50840eae041c15b7153e4124e13d30d1f07a000000c179d97a6a303df6c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "src/didactopus/auth.py", + "stripped_member": "src/didactopus/auth.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/auth.py", + "action": "overwrite", + "sha256": "554a347973d3beec8511c99074a583d9679c7d06d84b5573df4220c75830bd23", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "src/didactopus/engine.py", + "stripped_member": "src/didactopus/engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/engine.py", + "action": "overwrite", + "sha256": "16dadf1b4f917b1b2c67edfbf8bf494c6753c3d6269778832daeea8795a4022d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "src/didactopus/repository.py", + "stripped_member": "src/didactopus/repository.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/repository.py", + "action": "overwrite", + "sha256": "80055c50235da4aa2d72b36a8152cdd7897f6eaa7320123302a9ac8340624c56", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "src/didactopus/worker.py", + "stripped_member": "src/didactopus/worker.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/worker.py", + "action": "overwrite", + "sha256": "4dc2ad94224943d12846b513aec761361f71340b2731a45957e7c38f0f8ac93e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "src/didactopus/seed.py", + "stripped_member": "src/didactopus/seed.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/seed.py", + "action": "overwrite", + "sha256": "5b5ce7bcc00d433658e92fd708aa18831750bb096d4bc9e3ca8dfbeb09d0b0a3", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "overwrite", + "sha256": "dabc4529e14e2d15cc6b8904551df4ab7c89a48dd099e72ea3985b985d5ce448", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/195-didactopus-dual-lane-policy-layer.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132030__200-didactopus-layout-aware-graph-engine-layer__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "b42d1888aee6a13e6e28f6f98497d5e2de010c1d358675fdbf1a9f89d5848088", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "e0a14240224dc76de36ccfc2a7174d2142769261265755ada11de62a6573e706", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "a57258ca90bd94fe2cebf8e4418ff1fb98d30dcfb93e078be84812a586a556bc", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "tests/test_scaffold_files.py", + "stripped_member": "tests/test_scaffold_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_scaffold_files.py", + "action": "overwrite", + "sha256": "d547ea58d282e7ca8cb5dd4fa781d113e4a2bc75d2d98d3ea2539ab257ea8ffa", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "53d2eee84c6871b3c67ca385d4a4fcc84f4f6c40c89fdc0db52e5219f94bcf4c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "webui/src/api.js", + "stripped_member": "webui/src/api.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/api.js", + "action": "overwrite", + "sha256": "5524317af807070d0431cc6266994f77514d21ef8e3dfd548c9da8a666538983", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "webui/src/authStore.js", + "stripped_member": "webui/src/authStore.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/authStore.js", + "action": "overwrite", + "sha256": "d26a0e35b95996613ef24574fdfc8d97bfd2df9d8ef9c1cff5833461d162c4a0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "a0d5dc6d83878e02fe6d1535dc1249c27c239fa0d8fe628fc71705efb9de9fe2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "070707d66861df1ccba67bc5ae30f5cf3bee7353d503a565473f7c78853ece8f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "65ed36aba6467cdb9ba343460c2e59d10dd190c86f9d8c4450cdb5cf616c502f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "src/didactopus/db.py", + "stripped_member": "src/didactopus/db.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/db.py", + "action": "overwrite", + "sha256": "5773454c709c7288aed68dbd73e3134deececb328214ff5cf649a51fd0ea7790", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "src/didactopus/orm.py", + "stripped_member": "src/didactopus/orm.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orm.py", + "action": "overwrite", + "sha256": "cc9a5857a38e4df2a5b1fe64517a18da39c6f943f96ec2dfc8dbb5957027632c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "overwrite", + "sha256": "776b8dc989bf431d120d4d4595f2802400f7786043235f6e6529321d75da5912", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "src/didactopus/auth.py", + "stripped_member": "src/didactopus/auth.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/auth.py", + "action": "overwrite", + "sha256": "554a347973d3beec8511c99074a583d9679c7d06d84b5573df4220c75830bd23", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "src/didactopus/engine.py", + "stripped_member": "src/didactopus/engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/engine.py", + "action": "overwrite", + "sha256": "9e4fb26876c397ad14c9959f77f6790968bcbb773c7924c21e62dafd3c326908", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "src/didactopus/repository.py", + "stripped_member": "src/didactopus/repository.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/repository.py", + "action": "overwrite", + "sha256": "2d465c15aecd6d5279a289edd81f7e9e3d47546adaf8f23cfc8526e5ff29a8e3", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "src/didactopus/export_svg.py", + "stripped_member": "src/didactopus/export_svg.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/export_svg.py", + "action": "overwrite", + "sha256": "8c92cbd263e4004fdbb55503b4da4dc496cc724c61dc55c405948daa5cf599e4", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "src/didactopus/seed.py", + "stripped_member": "src/didactopus/seed.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/seed.py", + "action": "overwrite", + "sha256": "66122cf457e3ae06f12144f3113dc9f700ab61a9fa38daf1b05754f087004a18", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "overwrite", + "sha256": "afabca195bb23fec0e1ea7c891217297f9c012a1feb653c0c9884371b40fd222", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/200-didactopus-layout-aware-graph-engine-layer.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/205-didactopus-learner-state-progression-update.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132033__205-didactopus-learner-state-progression-update__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/205-didactopus-learner-state-progression-update.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "99220c2e70bbf8159c26ca9be518cd9abffb3f722e47b68b9255df9d1a7d5e8b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/205-didactopus-learner-state-progression-update.zip", + "member": "samples/concepts.yaml", + "stripped_member": "samples/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/samples/concepts.yaml", + "action": "create", + "sha256": "2f4d7fba89a155898b3de77ec9d9bd07d4115dab804467cd98664070f4666171", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/205-didactopus-learner-state-progression-update.zip", + "member": "samples/learner_state.json", + "stripped_member": "samples/learner_state.json", + "dest": "/home/netuser/dev/Didactopus/samples/learner_state.json", + "action": "create", + "sha256": "a7097af8a9eca427b7afa0ea919c71b156c41a05ae2680ddc86d6647b769c917", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/205-didactopus-learner-state-progression-update.zip", + "member": "tests/test_progression_engine.py", + "stripped_member": "tests/test_progression_engine.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_progression_engine.py", + "action": "create", + "sha256": "48c8032c0e137542c6191f569d0bafceda90473a83d6932c111ecf50156173b8", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/205-didactopus-learner-state-progression-update.zip", + "member": "tests/test_readiness.py", + "stripped_member": "tests/test_readiness.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_readiness.py", + "action": "create", + "sha256": "90d6bbee88c0991e4850a4c06043658fc1be8992815e7aa9a8a9e2ee93889edf", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/205-didactopus-learner-state-progression-update.zip", + "member": "tests/test_recommendations.py", + "stripped_member": "tests/test_recommendations.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_recommendations.py", + "action": "create", + "sha256": "f580cbc906f2891b50277552a28bcbbdcc016fc4009cefb0362b2a5db8e1f6d7", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/205-didactopus-learner-state-progression-update.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/205-didactopus-learner-state-progression-update.zip", + "member": "src/didactopus/learner_state.py", + "stripped_member": "src/didactopus/learner_state.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/learner_state.py", + "action": "create", + "sha256": "b489447274d2123e95d38e88f188d1d7e77779fb3be34d5e646770278047e92f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/205-didactopus-learner-state-progression-update.zip", + "member": "src/didactopus/progression_engine.py", + "stripped_member": "src/didactopus/progression_engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/progression_engine.py", + "action": "create", + "sha256": "c69487c4d6d0bbe8fa3f38f6e6ece8d129137544fdd385cd48f7de591fb4f314", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/205-didactopus-learner-state-progression-update.zip", + "member": "src/didactopus/readiness.py", + "stripped_member": "src/didactopus/readiness.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/readiness.py", + "action": "create", + "sha256": "0bc447a1731c7d24c1374e63cad53064d8400e11770ed0b68f11fcc84f6ed7f4", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/205-didactopus-learner-state-progression-update.zip", + "member": "src/didactopus/recommendations.py", + "stripped_member": "src/didactopus/recommendations.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/recommendations.py", + "action": "create", + "sha256": "437a6c2135cb44f55e9d91d06bc89ad7584aa29445b1f342e19e204f449d032a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/205-didactopus-learner-state-progression-update.zip", + "member": "src/didactopus/sample_pack_loader.py", + "stripped_member": "src/didactopus/sample_pack_loader.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/sample_pack_loader.py", + "action": "create", + "sha256": "ce1321ac4299ec0f9bd82287e619a776a88f2fd4dc232d9ddefd85497802b3a2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/205-didactopus-learner-state-progression-update.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132036__210-didactopus-learning-animation-layer__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "f70b8b06d8e738a7df561f5eeca116067051a48abbb4d99575f09a413dee7634", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "c5218e1b8960c1868ab51a7c40974eaaa1a4c414cc9fd7d7145dde2a0acc24ea", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "66460951a548b563c5c8376292979c427b82a98881af81b351118497d28a8060", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "tests/test_scaffold_files.py", + "stripped_member": "tests/test_scaffold_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_scaffold_files.py", + "action": "overwrite", + "sha256": "422ed2af486b957f852c599c8c779d7838711f1d9f0ad9c1e03a5d223544753a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "53d2eee84c6871b3c67ca385d4a4fcc84f4f6c40c89fdc0db52e5219f94bcf4c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "webui/src/api.js", + "stripped_member": "webui/src/api.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/api.js", + "action": "overwrite", + "sha256": "74c2a5b929ad05df6c4b2a480fffcd96c2f1c7f503939b24e9dde3fd7cf19867", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "webui/src/authStore.js", + "stripped_member": "webui/src/authStore.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/authStore.js", + "action": "overwrite", + "sha256": "d26a0e35b95996613ef24574fdfc8d97bfd2df9d8ef9c1cff5833461d162c4a0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "c57275a3044508ff3cdf4a91336c0c72eace2f5f44c5babadb47cff8c5273732", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "8c29ad21db84bcf9589f95f33f9b66d80727ca72e96ed165f10f5450ab75d292", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "240dc5d84ff9c1ba44e9a6328927f240d44e9543f757831488ee5bd32e03be52", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "src/didactopus/db.py", + "stripped_member": "src/didactopus/db.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/db.py", + "action": "overwrite", + "sha256": "5773454c709c7288aed68dbd73e3134deececb328214ff5cf649a51fd0ea7790", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "src/didactopus/orm.py", + "stripped_member": "src/didactopus/orm.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orm.py", + "action": "overwrite", + "sha256": "b5cd9b8f6f70a033aceeaabcec9b4c600ac7b349e1ef1794a540cd918fe3fc19", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "overwrite", + "sha256": "883ea4f0671934f5ce75b2af8c394bbf43b26db8a152789567fdc0a8792e7bf7", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "src/didactopus/auth.py", + "stripped_member": "src/didactopus/auth.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/auth.py", + "action": "overwrite", + "sha256": "8b735b8e0b01a11e86f942d1a10449a62b29b70a0f2eebd94fd836040c2a2d5c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "src/didactopus/engine.py", + "stripped_member": "src/didactopus/engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/engine.py", + "action": "overwrite", + "sha256": "5c6eb308e7c36785d79e870eabdac91a8516c48ab1dca2f52c9570b38b7fa64a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "src/didactopus/repository.py", + "stripped_member": "src/didactopus/repository.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/repository.py", + "action": "overwrite", + "sha256": "21e540e141fc27f6cc5b4056ee355c91043e6cd58ad3deb38c8155b295baff46", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "src/didactopus/worker.py", + "stripped_member": "src/didactopus/worker.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/worker.py", + "action": "overwrite", + "sha256": "06d860240032eec72bcc4e8af927564583a45a2b246fe679093c43f19f1caf6b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "src/didactopus/seed.py", + "stripped_member": "src/didactopus/seed.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/seed.py", + "action": "overwrite", + "sha256": "9e82186352c858cfc75fbb014b0852970b3ea4fc963a609a4fc10f108f003bff", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "overwrite", + "sha256": "bebc2e40a24a02738fe4b3425c03351c3246afa0bf50d787a3300c31cb22a351", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/210-didactopus-learning-animation-layer.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/215-didactopus-live-learner-ui-prototype.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132039__215-didactopus-live-learner-ui-prototype__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/215-didactopus-live-learner-ui-prototype.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "99220c2e70bbf8159c26ca9be518cd9abffb3f722e47b68b9255df9d1a7d5e8b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/215-didactopus-live-learner-ui-prototype.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "62a0c718488bbd709ecd39c604c5faa5672bd24b25473a9df92a6ced3084b5b8", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/215-didactopus-live-learner-ui-prototype.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "da642fda8b6f8c5c2a0a2c2d5cfaa972762441aaaa73f9a45d9324a4f12f6b95", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/215-didactopus-live-learner-ui-prototype.zip", + "member": "tests/test_ui_files.py", + "stripped_member": "tests/test_ui_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_ui_files.py", + "action": "create", + "sha256": "a69dc7496c470c40d47161092455a358987484fa2072b4f7a71889f6b9ede808", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/215-didactopus-live-learner-ui-prototype.zip", + "member": "tests/test_python_scaffold.py", + "stripped_member": "tests/test_python_scaffold.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_python_scaffold.py", + "action": "create", + "sha256": "a6b1ed226ffec7bc0e22658019ae8266f6ae6dd37409f277927f1568da0ef7fc", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/215-didactopus-live-learner-ui-prototype.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "73c55856ad1e60177fa61d52a6841b2166b7b3d0a0276b1449cdd33e2bb6e421", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/215-didactopus-live-learner-ui-prototype.zip", + "member": "webui/src/domainData.js", + "stripped_member": "webui/src/domainData.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/domainData.js", + "action": "create", + "sha256": "680f889c5588a3e07a239d6f718d9a19eb2842d36d911566bb1ef788cd42edea", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/215-didactopus-live-learner-ui-prototype.zip", + "member": "webui/src/engine.js", + "stripped_member": "webui/src/engine.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/engine.js", + "action": "create", + "sha256": "84518dff7228ace172c5aa3ac60932362cd5b64f144bdbcc38679a8bcf18c05f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/215-didactopus-live-learner-ui-prototype.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "5f07fd8e73e1a5e70bbc9defc03ad756e2529d1d7a3a8d701bda123295419b5b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/215-didactopus-live-learner-ui-prototype.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "7b5a8526560de9921f59b2186cb6586b5a89048624dfd9c820f1c2bb323416d2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/215-didactopus-live-learner-ui-prototype.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/215-didactopus-live-learner-ui-prototype.zip", + "member": "src/didactopus/learner_state.py", + "stripped_member": "src/didactopus/learner_state.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/learner_state.py", + "action": "overwrite", + "sha256": "b489447274d2123e95d38e88f188d1d7e77779fb3be34d5e646770278047e92f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/215-didactopus-live-learner-ui-prototype.zip", + "member": "src/didactopus/progression_engine.py", + "stripped_member": "src/didactopus/progression_engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/progression_engine.py", + "action": "overwrite", + "sha256": "3cc4c26031e22a6c978ea504447f654ee65ca07f02f18bee202a2086bb3ab347", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/215-didactopus-live-learner-ui-prototype.zip", + "member": "src/didactopus/orchestration_notes.md", + "stripped_member": "src/didactopus/orchestration_notes.md", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orchestration_notes.md", + "action": "create", + "sha256": "3ff2fd270d459b71cf5f7a44734c02ee3854476199f0e7b6e7282961053130b4", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/215-didactopus-live-learner-ui-prototype.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132042__220-didactopus-media-rendering-pipeline-layer__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "b87db7a0d89a8df478bcb3b2105fab382d0eda49bdb9e7b52c043b8674025236", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "2cd54add68c63ec3f2a2e18bd19988d007f0b328fa0a51668493135c89207819", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "193f152943d28b9c4669166d481956df709f0d8b1abf24dd96631dfe9f997f23", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "tests/test_scaffold_files.py", + "stripped_member": "tests/test_scaffold_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_scaffold_files.py", + "action": "overwrite", + "sha256": "e775301a6314ebc1c1a0309b6bce7b8d7bc1b0dad2070bd519f5c9fe2f734d06", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "53d2eee84c6871b3c67ca385d4a4fcc84f4f6c40c89fdc0db52e5219f94bcf4c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "webui/src/api.js", + "stripped_member": "webui/src/api.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/api.js", + "action": "overwrite", + "sha256": "0b50c2501310fd475ddc8d9c03cd96400cb13125392cf94d113caa590460e3c2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "webui/src/authStore.js", + "stripped_member": "webui/src/authStore.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/authStore.js", + "action": "overwrite", + "sha256": "d26a0e35b95996613ef24574fdfc8d97bfd2df9d8ef9c1cff5833461d162c4a0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "31fb80bd75179a9589e86a8c8b13a5752bf9f29508687b91a0ecfe36cc6afbb4", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "0ec2862fc5ab7c62c7f33c3ab2710f9f2927c552105c7db52b8119122af572a9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "65ed36aba6467cdb9ba343460c2e59d10dd190c86f9d8c4450cdb5cf616c502f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "src/didactopus/db.py", + "stripped_member": "src/didactopus/db.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/db.py", + "action": "overwrite", + "sha256": "5773454c709c7288aed68dbd73e3134deececb328214ff5cf649a51fd0ea7790", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "src/didactopus/orm.py", + "stripped_member": "src/didactopus/orm.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orm.py", + "action": "overwrite", + "sha256": "cc9a5857a38e4df2a5b1fe64517a18da39c6f943f96ec2dfc8dbb5957027632c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "overwrite", + "sha256": "f906ab1279e1e82c6c10af452fbfb022cae3f604e71d7b03b6652a32dc1919d6", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "src/didactopus/auth.py", + "stripped_member": "src/didactopus/auth.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/auth.py", + "action": "overwrite", + "sha256": "554a347973d3beec8511c99074a583d9679c7d06d84b5573df4220c75830bd23", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "src/didactopus/engine.py", + "stripped_member": "src/didactopus/engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/engine.py", + "action": "overwrite", + "sha256": "9e4fb26876c397ad14c9959f77f6790968bcbb773c7924c21e62dafd3c326908", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "src/didactopus/repository.py", + "stripped_member": "src/didactopus/repository.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/repository.py", + "action": "overwrite", + "sha256": "2d465c15aecd6d5279a289edd81f7e9e3d47546adaf8f23cfc8526e5ff29a8e3", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "src/didactopus/export_svg.py", + "stripped_member": "src/didactopus/export_svg.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/export_svg.py", + "action": "overwrite", + "sha256": "66ac201888b73537cdf2ce3f4c6bb912e67f49679d82339378418575be0c3c27", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "src/didactopus/render_bundle.py", + "stripped_member": "src/didactopus/render_bundle.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/render_bundle.py", + "action": "overwrite", + "sha256": "617d91b2685202a57c2d9ff524ac05a0b8801a55d7095432cfb310de7bea3752", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "src/didactopus/seed.py", + "stripped_member": "src/didactopus/seed.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/seed.py", + "action": "overwrite", + "sha256": "66122cf457e3ae06f12144f3113dc9f700ab61a9fa38daf1b05754f087004a18", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "overwrite", + "sha256": "fc3aa220d61b6469c45992e12fb8eac8c88da890e15a723c2267081d34f3470d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/220-didactopus-media-rendering-pipeline-layer.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/225-didactopus-orchestration-ux-update.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132048__225-didactopus-orchestration-ux-update__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/225-didactopus-orchestration-ux-update.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "65207eedf7b5c6f811b28f2cbfaf0188552e236a967d2ab3031b49edf8716b09", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/225-didactopus-orchestration-ux-update.zip", + "member": "docs/ux-notes.md", + "stripped_member": "docs/ux-notes.md", + "dest": "/home/netuser/dev/Didactopus/docs/ux-notes.md", + "action": "create", + "sha256": "bd5453654207c99d87a4ce6ef19dc5eef7510e4d7ed19eb2ecf45f4d0c00ec56", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/225-didactopus-orchestration-ux-update.zip", + "member": "samples/concepts.yaml", + "stripped_member": "samples/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/samples/concepts.yaml", + "action": "overwrite", + "sha256": "2f4d7fba89a155898b3de77ec9d9bd07d4115dab804467cd98664070f4666171", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/225-didactopus-orchestration-ux-update.zip", + "member": "tests/test_onboarding.py", + "stripped_member": "tests/test_onboarding.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_onboarding.py", + "action": "create", + "sha256": "fefcb73c8f18ec64eb52cb0f3b9ef0d8259a2d0a37a13d9dac29d1c29a814d22", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/225-didactopus-orchestration-ux-update.zip", + "member": "tests/test_stop_criteria.py", + "stripped_member": "tests/test_stop_criteria.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_stop_criteria.py", + "action": "create", + "sha256": "4573cbd8be4ba165af25bf63b8170c63269678afe044df6067c303f24e867c82", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/225-didactopus-orchestration-ux-update.zip", + "member": "tests/test_orchestrator.py", + "stripped_member": "tests/test_orchestrator.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_orchestrator.py", + "action": "create", + "sha256": "8c65c5202ccff72f58fddf83d4543f5f3e36eb8b725a236b69b9c5fab63b2160", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/225-didactopus-orchestration-ux-update.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/225-didactopus-orchestration-ux-update.zip", + "member": "src/didactopus/learner_state.py", + "stripped_member": "src/didactopus/learner_state.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/learner_state.py", + "action": "overwrite", + "sha256": "b489447274d2123e95d38e88f188d1d7e77779fb3be34d5e646770278047e92f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/225-didactopus-orchestration-ux-update.zip", + "member": "src/didactopus/progression_engine.py", + "stripped_member": "src/didactopus/progression_engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/progression_engine.py", + "action": "overwrite", + "sha256": "3cc4c26031e22a6c978ea504447f654ee65ca07f02f18bee202a2086bb3ab347", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/225-didactopus-orchestration-ux-update.zip", + "member": "src/didactopus/readiness.py", + "stripped_member": "src/didactopus/readiness.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/readiness.py", + "action": "overwrite", + "sha256": "0bc447a1731c7d24c1374e63cad53064d8400e11770ed0b68f11fcc84f6ed7f4", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/225-didactopus-orchestration-ux-update.zip", + "member": "src/didactopus/recommendations.py", + "stripped_member": "src/didactopus/recommendations.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/recommendations.py", + "action": "overwrite", + "sha256": "8bb7b6ea5524ebf13d502efcb0da6fc7b3f4efb9b2ac97076ed6dd915048d328", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/225-didactopus-orchestration-ux-update.zip", + "member": "src/didactopus/orchestration_models.py", + "stripped_member": "src/didactopus/orchestration_models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orchestration_models.py", + "action": "create", + "sha256": "2acd2a148706f1b819f78f48053a0a1afd6c864ea105d777b038db0bf9c4291b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/225-didactopus-orchestration-ux-update.zip", + "member": "src/didactopus/onboarding.py", + "stripped_member": "src/didactopus/onboarding.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/onboarding.py", + "action": "create", + "sha256": "0715def1f0ef3ca45687e3ed28fe0315ed20c1ba5366a40d66d2253fa9fed12b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/225-didactopus-orchestration-ux-update.zip", + "member": "src/didactopus/ux_feedback.py", + "stripped_member": "src/didactopus/ux_feedback.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/ux_feedback.py", + "action": "create", + "sha256": "1c6674974440bade22028e565cd1e75d71a891a1cda02ac3f6315beac7c733c1", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/225-didactopus-orchestration-ux-update.zip", + "member": "src/didactopus/stop_criteria.py", + "stripped_member": "src/didactopus/stop_criteria.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/stop_criteria.py", + "action": "create", + "sha256": "33842bb1d8e9c3071eadb952db9968ce66a10ccd54fc81d3769969c1f4bdac62", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/225-didactopus-orchestration-ux-update.zip", + "member": "src/didactopus/orchestrator.py", + "stripped_member": "src/didactopus/orchestrator.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orchestrator.py", + "action": "create", + "sha256": "81817f8760660c3fe96c71fa556a79801bf99105a710cd942a052e94506bbbfc", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/225-didactopus-orchestration-ux-update.zip", + "member": "src/didactopus/demo_run.py", + "stripped_member": "src/didactopus/demo_run.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/demo_run.py", + "action": "create", + "sha256": "fafd7e7cd20c342b3adb47a9239f2aa8fd1e0fa50928d7e641d20f593879e00f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/225-didactopus-orchestration-ux-update.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/230-didactopus-pack-persistence-update.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132051__230-didactopus-pack-persistence-update__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/230-didactopus-pack-persistence-update.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "cd85afef4b144f324eded0e4885d1a970c115c6f63da884a290bf2ab92803f99", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/230-didactopus-pack-persistence-update.zip", + "member": "example-pack/pack.yaml", + "stripped_member": "example-pack/pack.yaml", + "dest": "/home/netuser/dev/Didactopus/example-pack/pack.yaml", + "action": "create", + "sha256": "8fc1d1e02decf75c4e955b4c852ee478c5a4a5d2dcbec60a23c655f696adf9aa", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/230-didactopus-pack-persistence-update.zip", + "member": "example-pack/concepts.yaml", + "stripped_member": "example-pack/concepts.yaml", + "dest": "/home/netuser/dev/Didactopus/example-pack/concepts.yaml", + "action": "create", + "sha256": "d22d9d2fa82513a03508c570e017756dc543032e91aa68b878015de70cea46c9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/230-didactopus-pack-persistence-update.zip", + "member": "example-pack/pack_compliance_manifest.json", + "stripped_member": "example-pack/pack_compliance_manifest.json", + "dest": "/home/netuser/dev/Didactopus/example-pack/pack_compliance_manifest.json", + "action": "create", + "sha256": "6694ceb21eaccb05f5623e5884b00f901cf9a8b74fe6b609eef8171c4d00c673", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/230-didactopus-pack-persistence-update.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "5d754824b88ee95711aedef8f57bba51770b1066d1c39294acd8d94342b9bd1c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/230-didactopus-pack-persistence-update.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "4cfe9d17720c3f06cff4be42c2e0278d53ab7a0debad1be264fbfe395e567a13", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/230-didactopus-pack-persistence-update.zip", + "member": "tests/test_pack_export.py", + "stripped_member": "tests/test_pack_export.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_pack_export.py", + "action": "overwrite", + "sha256": "8bae16058b8e9bff2a55d6443ed1109254820e6654dc84a4c27a8e111f39322b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/230-didactopus-pack-persistence-update.zip", + "member": "tests/test_ui_files.py", + "stripped_member": "tests/test_ui_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_ui_files.py", + "action": "overwrite", + "sha256": "49020bd0045b8fa7dac12a8a53e8e7e5b73d114222999c9c206ac88af6141a6f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/230-didactopus-pack-persistence-update.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "73c55856ad1e60177fa61d52a6841b2166b7b3d0a0276b1449cdd33e2bb6e421", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/230-didactopus-pack-persistence-update.zip", + "member": "webui/src/storage.js", + "stripped_member": "webui/src/storage.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/storage.js", + "action": "create", + "sha256": "98533070a240370b37a8425d8c734c717568403b9e2d67b3df8ff94c49f9f53e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/230-didactopus-pack-persistence-update.zip", + "member": "webui/src/engine.js", + "stripped_member": "webui/src/engine.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/engine.js", + "action": "overwrite", + "sha256": "6f30bf004c490ae643e50fc682dada6bdf5222a9afa9b4f704ae1613cc61fb1b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/230-didactopus-pack-persistence-update.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "6dbe026f11f0816bf6f18f5763bbd360c1db07d53eda43c35a368a64440d6f6c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/230-didactopus-pack-persistence-update.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "e9a707f23a2adf651a1874be9c59ebc0bc3c16346aaeed6da901907c66d3fc6f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/230-didactopus-pack-persistence-update.zip", + "member": "webui/public/packs/bayes-pack.json", + "stripped_member": "webui/public/packs/bayes-pack.json", + "dest": "/home/netuser/dev/Didactopus/webui/public/packs/bayes-pack.json", + "action": "create", + "sha256": "cdcedf1e3e9444e634f5ad2ed1c64dffefd70b7428603ecd58aa2242cf4609e2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/230-didactopus-pack-persistence-update.zip", + "member": "webui/public/packs/stats-pack.json", + "stripped_member": "webui/public/packs/stats-pack.json", + "dest": "/home/netuser/dev/Didactopus/webui/public/packs/stats-pack.json", + "action": "create", + "sha256": "7e387aee07646b47fc5de857dacca0f3caef74e48cf7c6d57da0808c903e9209", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/230-didactopus-pack-persistence-update.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/230-didactopus-pack-persistence-update.zip", + "member": "src/didactopus/pack_to_frontend.py", + "stripped_member": "src/didactopus/pack_to_frontend.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/pack_to_frontend.py", + "action": "create", + "sha256": "dc9e380b9e1ff8c7cb80abbffbd1cc82777236ea4ab2ba853c743a1bd8172a5b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/230-didactopus-pack-persistence-update.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132053__235-didactopus-productionization-scaffold__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "71f7b7ad9a9cfb1a9db10a87aa044a688dbc739cb61d4a8eb0d567e9b031f4e0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "docker-compose.yml", + "stripped_member": "docker-compose.yml", + "dest": "/home/netuser/dev/Didactopus/docker-compose.yml", + "action": "overwrite", + "sha256": "dbf866a8b9b38482470553446b684a24bf6a0b6133cba96b2a0029af9cea77a2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "Dockerfile", + "stripped_member": "Dockerfile", + "dest": "/home/netuser/dev/Didactopus/Dockerfile", + "action": "overwrite", + "sha256": "b0af038caecd8757053892a24644212166da23e82385023ea709967af1f8512c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "9f74b03228ee4b75bf3874cbd3f902852b9612122171a2a334cfdf39282576d9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "06c537840671cdfe55f0ba1ad60c738365939f181c53b467ec7ef722a9cd9427", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "tests/test_files.py", + "stripped_member": "tests/test_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_files.py", + "action": "create", + "sha256": "c843d5b9910ec82c49f711233e05d2f2da682ea63b71a7fec3bcdd7df5970635", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "53d2eee84c6871b3c67ca385d4a4fcc84f4f6c40c89fdc0db52e5219f94bcf4c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "c16f261cbb492ae41fe0b45608d0c73f40a418e752056a3526bc5c6089c3c74c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "ee30d9a48db810aafba925c6c8d774b381cf4555351baeac828ec88cd8e391f9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "69e78652baa89c5a55aa9d70aff05fbb658502107546965ecba1e3abae0f14db", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "src/didactopus/db.py", + "stripped_member": "src/didactopus/db.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/db.py", + "action": "overwrite", + "sha256": "5773454c709c7288aed68dbd73e3134deececb328214ff5cf649a51fd0ea7790", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "src/didactopus/orm.py", + "stripped_member": "src/didactopus/orm.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orm.py", + "action": "overwrite", + "sha256": "f29b4b34a76cd06c5a27028698b779f837dc7988531ac1d94c3939751767498a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "overwrite", + "sha256": "5bfbc5490259842a73aeb9a3f4182ed948c865341ed532b19c1593551dc020d7", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "src/didactopus/auth.py", + "stripped_member": "src/didactopus/auth.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/auth.py", + "action": "overwrite", + "sha256": "e3abaf693b4cb48b1e04923a92c3652bb6d01efd568e35c759de41b7d9de3dcb", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "src/didactopus/engine.py", + "stripped_member": "src/didactopus/engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/engine.py", + "action": "overwrite", + "sha256": "16dadf1b4f917b1b2c67edfbf8bf494c6753c3d6269778832daeea8795a4022d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "src/didactopus/repository.py", + "stripped_member": "src/didactopus/repository.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/repository.py", + "action": "overwrite", + "sha256": "dc413ce6c30a7a396ca07f58603871fb020902d3646f53cd065706bc0bb42244", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "src/didactopus/worker.py", + "stripped_member": "src/didactopus/worker.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/worker.py", + "action": "overwrite", + "sha256": "1c6274bd22b5da6601499938a79b16c6a2fed8ca3df13891c80bd99ec8088957", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "src/didactopus/seed.py", + "stripped_member": "src/didactopus/seed.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/seed.py", + "action": "overwrite", + "sha256": "1b749b1e9097e3212e73a47607a292f94326f3c991666d3deabb2f681447e81b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "overwrite", + "sha256": "34ac0b5956ed35ad20af37dd7ff4d9790d9ce2e6393b73778aa80c164a0f06ce", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/235-didactopus-productionization-scaffold.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132058__240-didactopus-review-governance-layer__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "ed3405034f233d4319d9a34b3cc2ecca587c481362afcd585b9c656f506da798", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "04089f3a5807a71fd10d03de5bcff2b2a89cd478efdba44fccbc1e042df7de94", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "7ccef9775595d8a1b748a42ca76c233522932130617c2f25d1a4836c97a6057c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "tests/test_scaffold_files.py", + "stripped_member": "tests/test_scaffold_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_scaffold_files.py", + "action": "overwrite", + "sha256": "422ed2af486b957f852c599c8c779d7838711f1d9f0ad9c1e03a5d223544753a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "53d2eee84c6871b3c67ca385d4a4fcc84f4f6c40c89fdc0db52e5219f94bcf4c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "webui/src/api.js", + "stripped_member": "webui/src/api.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/api.js", + "action": "overwrite", + "sha256": "9c4abb71cec35fd9bc16be6651c127c4a29fd5a5b8e00e88b5c182694d666aa9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "webui/src/authStore.js", + "stripped_member": "webui/src/authStore.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/authStore.js", + "action": "overwrite", + "sha256": "d26a0e35b95996613ef24574fdfc8d97bfd2df9d8ef9c1cff5833461d162c4a0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "18fb8538034b0c33d44fff10e22b30ac7c5a8556c7c515074a7186afcdc9f10c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "3a3406a3bf76aedfe99b3d7d21635ca329174dff8c86275d71d92d530ff50e6b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "23d092311fbefc5999518a7b92718e7435835c70839e663171b646bdba8006cd", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "src/didactopus/db.py", + "stripped_member": "src/didactopus/db.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/db.py", + "action": "overwrite", + "sha256": "5773454c709c7288aed68dbd73e3134deececb328214ff5cf649a51fd0ea7790", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "src/didactopus/orm.py", + "stripped_member": "src/didactopus/orm.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orm.py", + "action": "overwrite", + "sha256": "64e9cdc7c542382aae0c0d54aa26f0bbf9463e2a2cc9a0c7471544baedb032a6", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "overwrite", + "sha256": "4efdeb916d53b168762ba741e65a3a5644657896cd5ebcc2f0b93afb1a7a7fbf", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "src/didactopus/auth.py", + "stripped_member": "src/didactopus/auth.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/auth.py", + "action": "overwrite", + "sha256": "554a347973d3beec8511c99074a583d9679c7d06d84b5573df4220c75830bd23", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "src/didactopus/engine.py", + "stripped_member": "src/didactopus/engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/engine.py", + "action": "overwrite", + "sha256": "16dadf1b4f917b1b2c67edfbf8bf494c6753c3d6269778832daeea8795a4022d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "src/didactopus/repository.py", + "stripped_member": "src/didactopus/repository.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/repository.py", + "action": "overwrite", + "sha256": "fc7a8d92e298241039a1e360ef3bbfb36a0f2d853bcb0ac4a578451e2f62fd14", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "src/didactopus/worker.py", + "stripped_member": "src/didactopus/worker.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/worker.py", + "action": "overwrite", + "sha256": "4dc2ad94224943d12846b513aec761361f71340b2731a45957e7c38f0f8ac93e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "src/didactopus/seed.py", + "stripped_member": "src/didactopus/seed.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/seed.py", + "action": "overwrite", + "sha256": "d04c20ee0452e8b6022cdc062fb2f4a0cde8ed2b7ab9a888f0f4283cb49bad65", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "overwrite", + "sha256": "19db5f0f80b1d8c89e3bd9d4e24f277fb2831aaacde6bd41afe613aba35938a1", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/240-didactopus-review-governance-layer.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132101__245-didactopus-agent-audit-and-key-rotation-layer__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "f70b8b06d8e738a7df561f5eeca116067051a48abbb4d99575f09a413dee7634", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "990c48fcba62c3eb8b16a32d913c7e4a8d4ba0d4748bca1b88547b69ed88a743", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "3757303551ab2e91bd2646ea220814d6d539f97e28639a24a0133b9a74c0ba26", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "tests/test_scaffold_files.py", + "stripped_member": "tests/test_scaffold_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_scaffold_files.py", + "action": "overwrite", + "sha256": "422ed2af486b957f852c599c8c779d7838711f1d9f0ad9c1e03a5d223544753a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "53d2eee84c6871b3c67ca385d4a4fcc84f4f6c40c89fdc0db52e5219f94bcf4c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "webui/src/api.js", + "stripped_member": "webui/src/api.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/api.js", + "action": "overwrite", + "sha256": "dde6c92c197f055aeda9cc1297debfb4b0c4d29d5432cd6a0f11c54b3694ca42", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "webui/src/authStore.js", + "stripped_member": "webui/src/authStore.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/authStore.js", + "action": "overwrite", + "sha256": "d26a0e35b95996613ef24574fdfc8d97bfd2df9d8ef9c1cff5833461d162c4a0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "89e094b1fa94d553639e64b9aae072eaa4304ffa6342f0e620b4c1e14fc288a5", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "0ffad9aa72eadeace0ada6f4d186a5a7c179dcbe344a7c8dce61f6b7822d9001", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "240dc5d84ff9c1ba44e9a6328927f240d44e9543f757831488ee5bd32e03be52", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/db.py", + "stripped_member": "src/didactopus/db.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/db.py", + "action": "overwrite", + "sha256": "5773454c709c7288aed68dbd73e3134deececb328214ff5cf649a51fd0ea7790", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/orm.py", + "stripped_member": "src/didactopus/orm.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orm.py", + "action": "overwrite", + "sha256": "212ba1b78b8cc89ae98dc4e87e1e939c757d90e66f2c8eba20f0215525a50397", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "overwrite", + "sha256": "49d6622d7a8d54ee20da35e13af68ff916a74bc2a5e50afc082684a167ed43a6", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/auth.py", + "stripped_member": "src/didactopus/auth.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/auth.py", + "action": "overwrite", + "sha256": "8b735b8e0b01a11e86f942d1a10449a62b29b70a0f2eebd94fd836040c2a2d5c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/engine.py", + "stripped_member": "src/didactopus/engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/engine.py", + "action": "overwrite", + "sha256": "16dadf1b4f917b1b2c67edfbf8bf494c6753c3d6269778832daeea8795a4022d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/repository.py", + "stripped_member": "src/didactopus/repository.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/repository.py", + "action": "overwrite", + "sha256": "8003efba24554451f46739ab60af0a39991419e8bd2e1b2909efb42f5228ab93", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/worker.py", + "stripped_member": "src/didactopus/worker.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/worker.py", + "action": "overwrite", + "sha256": "06d860240032eec72bcc4e8af927564583a45a2b246fe679093c43f19f1caf6b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/seed.py", + "stripped_member": "src/didactopus/seed.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/seed.py", + "action": "overwrite", + "sha256": "b139db9f4a028b59e1dd1c3cfad978b59604030bc13d64a54e211fa5e0f4f4cf", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "overwrite", + "sha256": "05c5fd813d3dda6708eb882aa56ec713b82a65548ee7e10a20746ab7d953b36d", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/245-didactopus-agent-audit-and-key-rotation-layer.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132104__250-didactopus-artifact-lifecycle-and-knowledge-export-layer__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "FAQ.md", + "stripped_member": "FAQ.md", + "dest": "/home/netuser/dev/Didactopus/FAQ.md", + "action": "create", + "sha256": "6c70ab8cd64326d10b66190b7e7993fa66904b696a51bd622d02af1d2be9a4b9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "b26e62a3b02238d947a8828b3293eabd8ac37b17681bebcdde267201d4fae885", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "91bc73571b3cfcc45df619a92ee4811f82e8ab161049eee1f2f7db64df54ba94", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "74f73b2934cdbcf8f993dd9f47bbf8d5e52b6b0cb5f1e089a7f5211878f5ba9b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "tests/test_scaffold_files.py", + "stripped_member": "tests/test_scaffold_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_scaffold_files.py", + "action": "overwrite", + "sha256": "efb520f53264f4e6b911bc3f5238b02459d888ea040b157a5bddc4bab17399c0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "53d2eee84c6871b3c67ca385d4a4fcc84f4f6c40c89fdc0db52e5219f94bcf4c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "webui/src/api.js", + "stripped_member": "webui/src/api.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/api.js", + "action": "overwrite", + "sha256": "abfcfa2d63b02c019ccee4b4bdd28facb09e3de4f673ce1b815994b14f446bf9", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "webui/src/authStore.js", + "stripped_member": "webui/src/authStore.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/authStore.js", + "action": "overwrite", + "sha256": "d26a0e35b95996613ef24574fdfc8d97bfd2df9d8ef9c1cff5833461d162c4a0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "a5dc2b379131ba53fa6724a3312a410ca4ac8e55783f9307280359283bfd3a0f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "38a2aca49262a9df841f007b21e62a4f224e11e31531488434b8df9f976c0a45", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "65ed36aba6467cdb9ba343460c2e59d10dd190c86f9d8c4450cdb5cf616c502f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "src/didactopus/db.py", + "stripped_member": "src/didactopus/db.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/db.py", + "action": "overwrite", + "sha256": "5773454c709c7288aed68dbd73e3134deececb328214ff5cf649a51fd0ea7790", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "src/didactopus/orm.py", + "stripped_member": "src/didactopus/orm.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orm.py", + "action": "overwrite", + "sha256": "86bbf54741455ac6a4f657f8de218e4244de8d74cbf84094eae59510d57e4760", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "overwrite", + "sha256": "2c8bf2a6dc7baccbd30338646be31be7af2e11dc63f685b515e6cf8b405abecc", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "src/didactopus/auth.py", + "stripped_member": "src/didactopus/auth.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/auth.py", + "action": "overwrite", + "sha256": "554a347973d3beec8511c99074a583d9679c7d06d84b5573df4220c75830bd23", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "src/didactopus/engine.py", + "stripped_member": "src/didactopus/engine.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/engine.py", + "action": "overwrite", + "sha256": "9e4fb26876c397ad14c9959f77f6790968bcbb773c7924c21e62dafd3c326908", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "src/didactopus/export_svg.py", + "stripped_member": "src/didactopus/export_svg.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/export_svg.py", + "action": "overwrite", + "sha256": "66ac201888b73537cdf2ce3f4c6bb912e67f49679d82339378418575be0c3c27", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "src/didactopus/render_bundle.py", + "stripped_member": "src/didactopus/render_bundle.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/render_bundle.py", + "action": "overwrite", + "sha256": "d448e20bd0829a12b1e35b0c2729396db5ddf6ab6e12aefdc322638dcf3793ad", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "src/didactopus/knowledge_export.py", + "stripped_member": "src/didactopus/knowledge_export.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/knowledge_export.py", + "action": "create", + "sha256": "9f674593d60658c9c4565e202b3d7f9b190c72b723499b2a344a2c63e7fd158c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "src/didactopus/worker.py", + "stripped_member": "src/didactopus/worker.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/worker.py", + "action": "overwrite", + "sha256": "f812645e370282683a9deedb3d6e0f758d7b46bc545018a7a61a3212b3b3f971", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "src/didactopus/repository.py", + "stripped_member": "src/didactopus/repository.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/repository.py", + "action": "overwrite", + "sha256": "843373763a12a14ebebbfee7f54fe5759621257eacd447ef0026886069d1682c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "src/didactopus/seed.py", + "stripped_member": "src/didactopus/seed.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/seed.py", + "action": "overwrite", + "sha256": "66122cf457e3ae06f12144f3113dc9f700ab61a9fa38daf1b05754f087004a18", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "overwrite", + "sha256": "c77801a557e897a1a8415538f543a73622473c770dfe79846784a30656d5c440", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/250-didactopus-artifact-lifecycle-and-knowledge-export-layer.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/255-didactopus-docs-update.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132106__255-didactopus-docs-update__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/255-didactopus-docs-update.zip", + "member": "FAQ.md", + "stripped_member": "FAQ.md", + "dest": "/home/netuser/dev/Didactopus/FAQ.md", + "action": "overwrite", + "sha256": "c46dd7ef62a0f3f9a11407b3e5f2942fe9a4e7c065bbbf36c1e90d9ff7677bb6", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/255-didactopus-docs-update.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/260-didactopus-object-versioning-and-export-layer.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132109__260-didactopus-object-versioning-and-export-layer__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/260-didactopus-object-versioning-and-export-layer.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "7c4e09c9b8534a647aa5ab966add5bf42ee2ba25dbcc30a10290d857d0b5a6b0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/260-didactopus-object-versioning-and-export-layer.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "0cf174187aa17db5d18af5d5f5b55ee10bf5690afcd8fbba508bedaf67605fb4", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/260-didactopus-object-versioning-and-export-layer.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "e71992d400dc81819836ce056adce762af878cc5fd7bcdf16bb5157f51c563c8", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/260-didactopus-object-versioning-and-export-layer.zip", + "member": "tests/test_scaffold_files.py", + "stripped_member": "tests/test_scaffold_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_scaffold_files.py", + "action": "overwrite", + "sha256": "13e9a97b9eb922997b5d732349199ef31de198877c1235c14c8b988d96816540", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/260-didactopus-object-versioning-and-export-layer.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "53d2eee84c6871b3c67ca385d4a4fcc84f4f6c40c89fdc0db52e5219f94bcf4c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/260-didactopus-object-versioning-and-export-layer.zip", + "member": "webui/src/api.js", + "stripped_member": "webui/src/api.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/api.js", + "action": "overwrite", + "sha256": "cf577fae7de4c94ab73216f77c195e2bd4cc55a1cd433af26f76933dc0699886", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/260-didactopus-object-versioning-and-export-layer.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "93e33556bc8b58c528b4b416633dc575f98777f3ac7cfcb6885f96d043d793ec", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/260-didactopus-object-versioning-and-export-layer.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "2fd9c1023782645e403b50675b388755d5b1b4d72bd1747621f119fb2c5a7b8b", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/260-didactopus-object-versioning-and-export-layer.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/260-didactopus-object-versioning-and-export-layer.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "65ed36aba6467cdb9ba343460c2e59d10dd190c86f9d8c4450cdb5cf616c502f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/260-didactopus-object-versioning-and-export-layer.zip", + "member": "src/didactopus/db.py", + "stripped_member": "src/didactopus/db.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/db.py", + "action": "overwrite", + "sha256": "5773454c709c7288aed68dbd73e3134deececb328214ff5cf649a51fd0ea7790", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/260-didactopus-object-versioning-and-export-layer.zip", + "member": "src/didactopus/orm.py", + "stripped_member": "src/didactopus/orm.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orm.py", + "action": "overwrite", + "sha256": "7bdeb8b9b05e3ed98f084944bfe09a2340c10889f732d018b58f659f36c5ed1a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/260-didactopus-object-versioning-and-export-layer.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "overwrite", + "sha256": "b965a76355b85163c2e31ddcab7c6baff7aff8290e17f453f4e799eb24e355b2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/260-didactopus-object-versioning-and-export-layer.zip", + "member": "src/didactopus/auth.py", + "stripped_member": "src/didactopus/auth.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/auth.py", + "action": "overwrite", + "sha256": "554a347973d3beec8511c99074a583d9679c7d06d84b5573df4220c75830bd23", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/260-didactopus-object-versioning-and-export-layer.zip", + "member": "src/didactopus/repository.py", + "stripped_member": "src/didactopus/repository.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/repository.py", + "action": "overwrite", + "sha256": "bd04284d352ee268e62e68a6fe249073e2d5cbf15ee7eef77fef3818595829cd", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/260-didactopus-object-versioning-and-export-layer.zip", + "member": "src/didactopus/synthesis.py", + "stripped_member": "src/didactopus/synthesis.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/synthesis.py", + "action": "create", + "sha256": "9cc1cdc1e8ecaabf0496ab1953b9570d8692c7c00c0e80a0ebcecd762ee9c291", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/260-didactopus-object-versioning-and-export-layer.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "overwrite", + "sha256": "21843e4bb9d8040a7288921c860e73fdf8b68ebf52b404614c88c7b6508c3b99", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/260-didactopus-object-versioning-and-export-layer.zip", + "member": "src/didactopus/seed.py", + "stripped_member": "src/didactopus/seed.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/seed.py", + "action": "overwrite", + "sha256": "c14661b7bf2fbd10045a1646afd22aaad0e0484b9f063106657e0edb52c41e76", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/260-didactopus-object-versioning-and-export-layer.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/270-didactopus-promotion-target-objects-layer.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132112__270-didactopus-promotion-target-objects-layer__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/270-didactopus-promotion-target-objects-layer.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "f70b8b06d8e738a7df561f5eeca116067051a48abbb4d99575f09a413dee7634", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/270-didactopus-promotion-target-objects-layer.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "1f4b863be3f9bd75ef2feb97c63ade114a7b4bb67e345da9735aacaad3f3a88e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/270-didactopus-promotion-target-objects-layer.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "915d58aaf1f8f3e32dc1cc53b048f4e7c79e79f29e16e5774b1c3f1f6d50b362", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/270-didactopus-promotion-target-objects-layer.zip", + "member": "tests/test_scaffold_files.py", + "stripped_member": "tests/test_scaffold_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_scaffold_files.py", + "action": "overwrite", + "sha256": "960c34584c927fdfb623e3f5164a155a4245c944c0cd72816acfefe548faf2d2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/270-didactopus-promotion-target-objects-layer.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "53d2eee84c6871b3c67ca385d4a4fcc84f4f6c40c89fdc0db52e5219f94bcf4c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/270-didactopus-promotion-target-objects-layer.zip", + "member": "webui/src/api.js", + "stripped_member": "webui/src/api.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/api.js", + "action": "overwrite", + "sha256": "67a65f70905a81a08658681ae868cdfdcf08f18862d5d489ca486b0cf9262a3e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/270-didactopus-promotion-target-objects-layer.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "3dce4364d2a668bff3f5d23cc5e0c2e68e1d07c3937b2027125c4ab93bfe9443", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/270-didactopus-promotion-target-objects-layer.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "73ff3de10d17a9328ab065e7ef8e9960c998396b2b10c1bd7cccfee242f66fda", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/270-didactopus-promotion-target-objects-layer.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/270-didactopus-promotion-target-objects-layer.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "65ed36aba6467cdb9ba343460c2e59d10dd190c86f9d8c4450cdb5cf616c502f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/270-didactopus-promotion-target-objects-layer.zip", + "member": "src/didactopus/db.py", + "stripped_member": "src/didactopus/db.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/db.py", + "action": "overwrite", + "sha256": "5773454c709c7288aed68dbd73e3134deececb328214ff5cf649a51fd0ea7790", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/270-didactopus-promotion-target-objects-layer.zip", + "member": "src/didactopus/orm.py", + "stripped_member": "src/didactopus/orm.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orm.py", + "action": "overwrite", + "sha256": "61c3a74edae7fc948f2a3c74e43192271d83a6bec45c43702bc1dc003ca955fa", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/270-didactopus-promotion-target-objects-layer.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "overwrite", + "sha256": "e1881d8b9f75a2993502fe3a89fbc5fb679ad3dd2e8356c557f32ba44bf50057", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/270-didactopus-promotion-target-objects-layer.zip", + "member": "src/didactopus/auth.py", + "stripped_member": "src/didactopus/auth.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/auth.py", + "action": "overwrite", + "sha256": "554a347973d3beec8511c99074a583d9679c7d06d84b5573df4220c75830bd23", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/270-didactopus-promotion-target-objects-layer.zip", + "member": "src/didactopus/repository.py", + "stripped_member": "src/didactopus/repository.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/repository.py", + "action": "overwrite", + "sha256": "1ec7b27db2c0eb3626f6b6ad70b2c7fd3a2c5ca3f7f49332cc76fe21e22fe0b3", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/270-didactopus-promotion-target-objects-layer.zip", + "member": "src/didactopus/synthesis.py", + "stripped_member": "src/didactopus/synthesis.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/synthesis.py", + "action": "overwrite", + "sha256": "e95de16e23f477f1c23f893444440a89410515caa7b0089419e301b9fa244e1c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/270-didactopus-promotion-target-objects-layer.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "overwrite", + "sha256": "a34b3d27932ae4416def5f3b852717ef798fbbbfaf25c6ade38efc4a83280caa", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/270-didactopus-promotion-target-objects-layer.zip", + "member": "src/didactopus/seed.py", + "stripped_member": "src/didactopus/seed.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/seed.py", + "action": "overwrite", + "sha256": "c14661b7bf2fbd10045a1646afd22aaad0e0484b9f063106657e0edb52c41e76", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/270-didactopus-promotion-target-objects-layer.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/275-didactopus-review-promotion-and-synthesis-engine.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132115__275-didactopus-review-promotion-and-synthesis-engine__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/275-didactopus-review-promotion-and-synthesis-engine.zip", + "member": "FAQ.md", + "stripped_member": "FAQ.md", + "dest": "/home/netuser/dev/Didactopus/FAQ.md", + "action": "overwrite", + "sha256": "57d191670b6f11b6e1de5fb4a724a0219552aef73db014d8569b1e4f4ec2508f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/275-didactopus-review-promotion-and-synthesis-engine.zip", + "member": "docs/review_and_promotion_workflow.md", + "stripped_member": "docs/review_and_promotion_workflow.md", + "dest": "/home/netuser/dev/Didactopus/docs/review_and_promotion_workflow.md", + "action": "create", + "sha256": "1680d4871560a441e2330d3a7b527acdf63baa4f140ed672e58824cad4d8f05a", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/275-didactopus-review-promotion-and-synthesis-engine.zip", + "member": "docs/synthesis_engine_architecture.md", + "stripped_member": "docs/synthesis_engine_architecture.md", + "dest": "/home/netuser/dev/Didactopus/docs/synthesis_engine_architecture.md", + "action": "create", + "sha256": "5c44d1ec8f9910439dce5a711f7d2ea7f50263f3a76aab62580933416e8b2aa8", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/275-didactopus-review-promotion-and-synthesis-engine.zip", + "member": "docs/data_models.md", + "stripped_member": "docs/data_models.md", + "dest": "/home/netuser/dev/Didactopus/docs/data_models.md", + "action": "create", + "sha256": "b6daece30f98521e42475110fb2d749a3c3e105c0b659e4906bd67862f39e864", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/275-didactopus-review-promotion-and-synthesis-engine.zip", + "member": "docs/api_outline.md", + "stripped_member": "docs/api_outline.md", + "dest": "/home/netuser/dev/Didactopus/docs/api_outline.md", + "action": "create", + "sha256": "dde226ae3a7cd283ca35fe29b51f887100735948817c873d84dce77aadfffa69", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/275-didactopus-review-promotion-and-synthesis-engine.zip", + "member": "docs/ui_visualization_notes.md", + "stripped_member": "docs/ui_visualization_notes.md", + "dest": "/home/netuser/dev/Didactopus/docs/ui_visualization_notes.md", + "action": "create", + "sha256": "56b57388194513162ca20bc1b35e084f3df3823818e5056fd27379e3f8a374a4", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/275-didactopus-review-promotion-and-synthesis-engine.zip", + "member": "docs/architecture_summary.json", + "stripped_member": "docs/architecture_summary.json", + "dest": "/home/netuser/dev/Didactopus/docs/architecture_summary.json", + "action": "create", + "sha256": "391c43ba566978d001f839c967f5caf8dd1bc090565ac3978da1196dbcfbab03", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/275-didactopus-review-promotion-and-synthesis-engine.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/280-didactopus-review-workbench-and-synthesis-scaffold.zip", + "member": "README.md", + "stripped_member": "README.md", + "action": "archive_readme", + "archived_to": "/home/netuser/dev/Didactopus/.update_readmes/20260314_132120__280-didactopus-review-workbench-and-synthesis-scaffold__README.md", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/280-didactopus-review-workbench-and-synthesis-scaffold.zip", + "member": "pyproject.toml", + "stripped_member": "pyproject.toml", + "dest": "/home/netuser/dev/Didactopus/pyproject.toml", + "action": "overwrite", + "sha256": "f70b8b06d8e738a7df561f5eeca116067051a48abbb4d99575f09a413dee7634", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/280-didactopus-review-workbench-and-synthesis-scaffold.zip", + "member": "webui/package.json", + "stripped_member": "webui/package.json", + "dest": "/home/netuser/dev/Didactopus/webui/package.json", + "action": "overwrite", + "sha256": "c5d4ed5cec5c5c7d02f8eb5314c2d2a197af1e1fd375df7092eb58d16167b3f0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/280-didactopus-review-workbench-and-synthesis-scaffold.zip", + "member": "webui/index.html", + "stripped_member": "webui/index.html", + "dest": "/home/netuser/dev/Didactopus/webui/index.html", + "action": "overwrite", + "sha256": "6475204f532345e03fcdf5c594eff9cd0e25e1d3b248663394ede65c14f2381e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/280-didactopus-review-workbench-and-synthesis-scaffold.zip", + "member": "tests/test_scaffold_files.py", + "stripped_member": "tests/test_scaffold_files.py", + "dest": "/home/netuser/dev/Didactopus/tests/test_scaffold_files.py", + "action": "overwrite", + "sha256": "960c34584c927fdfb623e3f5164a155a4245c944c0cd72816acfefe548faf2d2", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/280-didactopus-review-workbench-and-synthesis-scaffold.zip", + "member": "webui/src/main.jsx", + "stripped_member": "webui/src/main.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/main.jsx", + "action": "overwrite", + "sha256": "53d2eee84c6871b3c67ca385d4a4fcc84f4f6c40c89fdc0db52e5219f94bcf4c", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/280-didactopus-review-workbench-and-synthesis-scaffold.zip", + "member": "webui/src/api.js", + "stripped_member": "webui/src/api.js", + "dest": "/home/netuser/dev/Didactopus/webui/src/api.js", + "action": "overwrite", + "sha256": "af3c63914077d34f14a9cd29d0f62ea56c82bfbf710e4f5af28769274eee181e", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/280-didactopus-review-workbench-and-synthesis-scaffold.zip", + "member": "webui/src/App.jsx", + "stripped_member": "webui/src/App.jsx", + "dest": "/home/netuser/dev/Didactopus/webui/src/App.jsx", + "action": "overwrite", + "sha256": "0a525c3579367406502a0e6e06a95fcf8ce197147cf7771b8e994460e7ab6760", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/280-didactopus-review-workbench-and-synthesis-scaffold.zip", + "member": "webui/src/styles.css", + "stripped_member": "webui/src/styles.css", + "dest": "/home/netuser/dev/Didactopus/webui/src/styles.css", + "action": "overwrite", + "sha256": "e0533e4daee5a26587270940ad2b429e57ab6c4681db93755e373046fff99dcd", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/280-didactopus-review-workbench-and-synthesis-scaffold.zip", + "member": "src/didactopus/__init__.py", + "stripped_member": "src/didactopus/__init__.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/__init__.py", + "action": "overwrite", + "sha256": "20c8e430ededc2143396e553a3c67aac4ec483ef54efdfcb18a31cb162ca0646", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/280-didactopus-review-workbench-and-synthesis-scaffold.zip", + "member": "src/didactopus/config.py", + "stripped_member": "src/didactopus/config.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/config.py", + "action": "overwrite", + "sha256": "65ed36aba6467cdb9ba343460c2e59d10dd190c86f9d8c4450cdb5cf616c502f", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/280-didactopus-review-workbench-and-synthesis-scaffold.zip", + "member": "src/didactopus/db.py", + "stripped_member": "src/didactopus/db.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/db.py", + "action": "overwrite", + "sha256": "5773454c709c7288aed68dbd73e3134deececb328214ff5cf649a51fd0ea7790", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/280-didactopus-review-workbench-and-synthesis-scaffold.zip", + "member": "src/didactopus/orm.py", + "stripped_member": "src/didactopus/orm.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/orm.py", + "action": "overwrite", + "sha256": "c3cc3632a9982158150d3dc4adc7b0d8ffc40aefd7d266e1728db893c0d6a9eb", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/280-didactopus-review-workbench-and-synthesis-scaffold.zip", + "member": "src/didactopus/models.py", + "stripped_member": "src/didactopus/models.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/models.py", + "action": "overwrite", + "sha256": "48ca15ac25e0afc7d75e521743897ad78877c5195d31fee3c535ca293dc13c95", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/280-didactopus-review-workbench-and-synthesis-scaffold.zip", + "member": "src/didactopus/auth.py", + "stripped_member": "src/didactopus/auth.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/auth.py", + "action": "overwrite", + "sha256": "554a347973d3beec8511c99074a583d9679c7d06d84b5573df4220c75830bd23", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/280-didactopus-review-workbench-and-synthesis-scaffold.zip", + "member": "src/didactopus/repository.py", + "stripped_member": "src/didactopus/repository.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/repository.py", + "action": "overwrite", + "sha256": "663102f06d6c6ae05016d06d32b77106a9819b66f9bc2e6096687bbfd3075ee0", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/280-didactopus-review-workbench-and-synthesis-scaffold.zip", + "member": "src/didactopus/synthesis.py", + "stripped_member": "src/didactopus/synthesis.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/synthesis.py", + "action": "overwrite", + "sha256": "187b2e0b9f4fd8108fe60429ebc0fd297fe48d3b692bf2a97266a70b5974d8af", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/280-didactopus-review-workbench-and-synthesis-scaffold.zip", + "member": "src/didactopus/api.py", + "stripped_member": "src/didactopus/api.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/api.py", + "action": "overwrite", + "sha256": "810b09aa43be53024de17acf68956b6781d0a82f099f28490d17efd4cfca2396", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/280-didactopus-review-workbench-and-synthesis-scaffold.zip", + "member": "src/didactopus/seed.py", + "stripped_member": "src/didactopus/seed.py", + "dest": "/home/netuser/dev/Didactopus/src/didactopus/seed.py", + "action": "overwrite", + "sha256": "c14661b7bf2fbd10045a1646afd22aaad0e0484b9f063106657e0edb52c41e76", + "strip_depth": 0 + }, + { + "zip": "/home/netuser/Downloads/Didactopus-extra/280-didactopus-review-workbench-and-synthesis-scaffold.zip", + "action": "git_commit", + "status": "skipped_or_failed", + "message": "git commit failed: Author identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n git config --global user.email \"you@example.com\"\n git config --global user.name \"Your Name\"\n\nto set your account's default identity.\nOmit --global to set the identity only in this repository.\n\nfatal: unable to auto-detect email address (got 'netuser@nerdanel.(none)')" + } +] \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 10e087d..28c92b4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,6 @@ FROM python:3.11-slim - WORKDIR /app -COPY pyproject.toml README.md /app/ +COPY pyproject.toml /app/pyproject.toml COPY src /app/src -COPY configs /app/configs -COPY domain-packs /app/domain-packs -RUN pip install --no-cache-dir -e . -CMD ["python", "-m", "didactopus.main", "--domain", "statistics", "--goal", "practical mastery"] +RUN pip install --no-cache-dir . +CMD ["didactopus-api"] diff --git a/FAQ.md b/FAQ.md new file mode 100644 index 0000000..30d7f69 --- /dev/null +++ b/FAQ.md @@ -0,0 +1,88 @@ +# Didactopus FAQ + +## What is Didactopus for? + +Didactopus helps represent learning as a knowledge graph with evidence, mastery, +artifacts, and reusable outputs. It supports both learners and the systems that +author, review, and improve learning materials. + +## Is it only for AI learners? + +No. It is built for: + +- human learners +- AI learners +- hybrid workflows where AI and humans both contribute + +## Why emphasize synthesis? + +Because understanding often improves when learners recognize structural overlap +between different domains. Transfer, analogy, and conceptual reuse are central to +real intellectual progress. + +Examples include: + +- entropy in thermodynamics and information theory +- drift in population genetics and random walks +- feedback in engineering, biology, and machine learning + +Didactopus tries to surface these overlaps rather than treating subjects as sealed +containers. + +## Why not automatically trust learner-derived knowledge? + +Learner-derived knowledge can be valuable, but it still needs review, +validation, and provenance. A learner may discover something surprising and +useful, but the system should preserve both usefulness and caution. + +## What can learner-derived knowledge become? + +Depending on review outcome, it can be promoted into: + +- accepted pack improvements +- curriculum drafts +- reusable skill bundles +- archived but unadopted suggestions + +## What is the review-and-promotion workflow? + +It is the process by which exported learner observations are triaged, reviewed, +validated, and either promoted or archived. + +## What is the synthesis engine? + +The synthesis engine analyzes concept graphs and learner evidence to identify +candidate conceptual overlaps, analogies, and transferable structures across +packs. + +## Can Didactopus produce traditional educational outputs? + +Yes. Knowledge exports can seed: + +- lesson outlines +- study guides +- exercise sets +- instructor notes +- curriculum maps + +## Can Didactopus produce AI skill-like outputs? + +Yes. Structured exports can support: + +- skill manifests +- evaluation checklists +- failure-mode notes +- canonical examples +- prerequisite maps + +## What happens to artifacts over time? + +Artifacts can be: + +- retained +- archived +- expired +- soft-deleted + +Retention policy support is included so temporary debugging products and durable +portfolio artifacts can be treated differently. diff --git a/bad-generated-pack/concepts.yaml b/bad-generated-pack/concepts.yaml new file mode 100644 index 0000000..1c3e587 --- /dev/null +++ b/bad-generated-pack/concepts.yaml @@ -0,0 +1,13 @@ +concepts: + - id: prior-and-posterior + title: Prior and Posterior + description: Beliefs before and after evidence. + prerequisites: [] + - id: posterior-analysis + title: Posterior Analysis + description: Beliefs before and after evidence. + prerequisites: [] + - id: statistics-and-probability + title: Statistics and Probability + description: General overview. + prerequisites: [] diff --git a/bad-generated-pack/evaluator.yaml b/bad-generated-pack/evaluator.yaml new file mode 100644 index 0000000..33da936 --- /dev/null +++ b/bad-generated-pack/evaluator.yaml @@ -0,0 +1,5 @@ +dimensions: + - name: typography + description: visual polish and typesetting +evidence_types: + - page layout diff --git a/bad-generated-pack/mastery_ledger.yaml b/bad-generated-pack/mastery_ledger.yaml new file mode 100644 index 0000000..174d311 --- /dev/null +++ b/bad-generated-pack/mastery_ledger.yaml @@ -0,0 +1,5 @@ +entry_schema: + concept_id: str + score: float +dimension_mappings: {} +evidence_type_mappings: {} diff --git a/bad-generated-pack/pack.yaml b/bad-generated-pack/pack.yaml new file mode 100644 index 0000000..590cbc9 --- /dev/null +++ b/bad-generated-pack/pack.yaml @@ -0,0 +1,3 @@ +name: broken-pack +display_name: Broken Pack +version: 0.1.0-draft diff --git a/bad-generated-pack/projects.yaml b/bad-generated-pack/projects.yaml new file mode 100644 index 0000000..4d053f9 --- /dev/null +++ b/bad-generated-pack/projects.yaml @@ -0,0 +1 @@ +projects: [] diff --git a/bad-generated-pack/roadmap.yaml b/bad-generated-pack/roadmap.yaml new file mode 100644 index 0000000..1b1c7b4 --- /dev/null +++ b/bad-generated-pack/roadmap.yaml @@ -0,0 +1,9 @@ +stages: + - id: stage-1 + title: Foundations + concepts: + - statistics-and-probability + - id: stage-2 + title: Advanced Inference + concepts: + - posterior-analysis diff --git a/bad-generated-pack/rubrics.yaml b/bad-generated-pack/rubrics.yaml new file mode 100644 index 0000000..cd1f79a --- /dev/null +++ b/bad-generated-pack/rubrics.yaml @@ -0,0 +1,5 @@ +rubrics: + - id: basic-rubric + title: Basic Rubric + criteria: + - correctness diff --git a/configs/config.example.yaml b/configs/config.example.yaml index 5544752..9c20d36 100644 --- a/configs/config.example.yaml +++ b/configs/config.example.yaml @@ -1,5 +1,8 @@ review: default_reviewer: "Wesley R. Elsberry" - allow_provisional_concepts: true write_promoted_pack: true - write_review_ledger: true +bridge: + host: "127.0.0.1" + port: 8765 + registry_path: "workspace_registry.json" + default_workspace_root: "workspaces" diff --git a/data/packs/bayes-pack.json b/data/packs/bayes-pack.json new file mode 100644 index 0000000..f0de85f --- /dev/null +++ b/data/packs/bayes-pack.json @@ -0,0 +1,53 @@ +{ + "id": "bayes-pack", + "title": "Bayesian Reasoning", + "subtitle": "Probability, evidence, updating, and model criticism.", + "level": "novice-friendly", + "concepts": [ + { + "id": "prior", + "title": "Prior", + "prerequisites": [], + "masteryDimension": "mastery", + "exerciseReward": "Prior badge earned" + }, + { + "id": "posterior", + "title": "Posterior", + "prerequisites": [ + "prior" + ], + "masteryDimension": "mastery", + "exerciseReward": "Posterior path opened" + }, + { + "id": "model-checking", + "title": "Model Checking", + "prerequisites": [ + "posterior" + ], + "masteryDimension": "mastery", + "exerciseReward": "Model-checking unlocked" + } + ], + "onboarding": { + "headline": "Start with a fast visible win", + "body": "Read one short orientation, answer one guided question, and leave with your first mastery marker.", + "checklist": [ + "Read the one-screen topic orientation", + "Answer one guided exercise", + "Write one explanation in your own words" + ] + }, + "compliance": { + "sources": 2, + "attributionRequired": true, + "shareAlikeRequired": true, + "noncommercialOnly": true, + "flags": [ + "share-alike", + "noncommercial", + "excluded-third-party-content" + ] + } +} \ No newline at end of file diff --git a/data/packs/stats-pack.json b/data/packs/stats-pack.json new file mode 100644 index 0000000..7ccf0fc --- /dev/null +++ b/data/packs/stats-pack.json @@ -0,0 +1,49 @@ +{ + "id": "stats-pack", + "title": "Introductory Statistics", + "subtitle": "Descriptive statistics, sampling, and inference.", + "level": "novice-friendly", + "concepts": [ + { + "id": "descriptive", + "title": "Descriptive Statistics", + "prerequisites": [], + "masteryDimension": "mastery", + "exerciseReward": "Descriptive tools unlocked" + }, + { + "id": "sampling", + "title": "Sampling", + "prerequisites": [ + "descriptive" + ], + "masteryDimension": "mastery", + "exerciseReward": "Sampling pathway opened" + }, + { + "id": "inference", + "title": "Inference", + "prerequisites": [ + "sampling" + ], + "masteryDimension": "mastery", + "exerciseReward": "Inference challenge unlocked" + } + ], + "onboarding": { + "headline": "Build your first useful data skill", + "body": "You will learn one concept that immediately helps you summarize real data.", + "checklist": [ + "See one worked example", + "Compute one short example yourself", + "Explain what the result means" + ] + }, + "compliance": { + "sources": 1, + "attributionRequired": true, + "shareAlikeRequired": false, + "noncommercialOnly": false, + "flags": [] + } +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index d44aaf8..03887ba 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,21 +1,37 @@ +version: "3.9" + services: - didactopus: + postgres: + image: postgres:16 + environment: + POSTGRES_DB: didactopus + POSTGRES_USER: didactopus + POSTGRES_PASSWORD: didactopus-dev-password + ports: + - "5432:5432" + volumes: + - ./ops/postgres-data:/var/lib/postgresql/data + + api: build: . - image: didactopus:dev + command: didactopus-api + environment: + DIDACTOPUS_DATABASE_URL: postgresql+psycopg://didactopus:didactopus-dev-password@postgres:5432/didactopus + DIDACTOPUS_JWT_SECRET: change-me + ports: + - "8011:8011" + depends_on: + - postgres volumes: - ./:/app - working_dir: /app - command: python -m didactopus.main --domain "statistics" --goal "practical mastery" + + worker: + build: . + command: didactopus-worker environment: - DIDACTOPUS_CONFIG: /app/configs/config.yaml - - ollama: - image: ollama/ollama:latest - profiles: ["local-llm"] - ports: - - "11434:11434" + DIDACTOPUS_DATABASE_URL: postgresql+psycopg://didactopus:didactopus-dev-password@postgres:5432/didactopus + DIDACTOPUS_JWT_SECRET: change-me + depends_on: + - postgres volumes: - - ollama-data:/root/.ollama - -volumes: - ollama-data: + - ./:/app diff --git a/docs/api_outline.md b/docs/api_outline.md new file mode 100644 index 0000000..f1da6fb --- /dev/null +++ b/docs/api_outline.md @@ -0,0 +1,43 @@ +# API Outline + +## Review-and-promotion workflow + +### Candidate intake +- `POST /api/knowledge-candidates` +- `GET /api/knowledge-candidates` +- `GET /api/knowledge-candidates/{candidate_id}` + +### Review +- `POST /api/knowledge-candidates/{candidate_id}/reviews` +- `GET /api/knowledge-candidates/{candidate_id}/reviews` + +### Promotion +- `POST /api/knowledge-candidates/{candidate_id}/promote` +- `GET /api/promotions` +- `GET /api/promotions/{promotion_id}` + +### Archive / reject +- `POST /api/knowledge-candidates/{candidate_id}/archive` +- `POST /api/knowledge-candidates/{candidate_id}/reject` + +## Synthesis engine + +### Candidate generation +- `POST /api/synthesis/run` +- `GET /api/synthesis/candidates` +- `GET /api/synthesis/candidates/{synthesis_id}` + +### Clusters +- `GET /api/synthesis/clusters` +- `GET /api/synthesis/clusters/{cluster_id}` + +### Promotion path +- `POST /api/synthesis/candidates/{synthesis_id}/promote` + +## Artifact lifecycle additions +- `GET /api/artifacts/{artifact_id}/download` +- `POST /api/artifacts/{artifact_id}/retention` +- `DELETE /api/artifacts/{artifact_id}` + +## Learner knowledge export +- `POST /api/learners/{learner_id}/knowledge-export/{pack_id}` diff --git a/docs/architecture_summary.json b/docs/architecture_summary.json new file mode 100644 index 0000000..33ce574 --- /dev/null +++ b/docs/architecture_summary.json @@ -0,0 +1,19 @@ +{ + "new_workstreams": [ + "review_and_promotion_workflow", + "synthesis_engine" + ], + "promotion_targets": [ + "accepted_pack_improvements", + "curriculum_drafts", + "reusable_skill_bundles", + "archived_unadopted_suggestions" + ], + "synthesis_signals": [ + "semantic_similarity", + "structural_similarity", + "learner_trajectory", + "review_history", + "novelty" + ] +} \ No newline at end of file diff --git a/docs/attribution-and-provenance.md b/docs/attribution-and-provenance.md new file mode 100644 index 0000000..8746b22 --- /dev/null +++ b/docs/attribution-and-provenance.md @@ -0,0 +1,35 @@ +# Attribution and Provenance in Didactopus + +A Didactopus pack that is built from external educational material should carry: +- source identity +- source URL +- creator / publisher +- license identifier +- license URL +- adaptation status +- attribution text +- exclusion notes +- retrieval date + +## Why both machine-readable and human-readable artifacts? + +Machine-readable provenance supports: +- validation +- export pipelines +- automated NOTICE/ATTRIBUTION generation +- future audit tools + +Human-readable attribution supports: +- repository inspection +- redistribution review +- transparency for maintainers and learners + +## Recommended policy + +Every ingested source record should answer: +1. What is the source? +2. Who published it? +3. Under what license? +4. Was the source adapted, excerpted, transformed, or only referenced? +5. Are any subcomponents excluded from the main license? +6. What attribution text should be shown downstream? diff --git a/docs/coverage-alignment.md b/docs/coverage-alignment.md new file mode 100644 index 0000000..0a2ac0f --- /dev/null +++ b/docs/coverage-alignment.md @@ -0,0 +1,3 @@ +# Coverage and Alignment QA + +This layer asks whether a domain pack's instructional elements actually line up. diff --git a/docs/curriculum-path-quality.md b/docs/curriculum-path-quality.md new file mode 100644 index 0000000..74167db --- /dev/null +++ b/docs/curriculum-path-quality.md @@ -0,0 +1,13 @@ +# Curriculum Path Quality Layer + +This layer analyzes roadmap and project structure as a learner-facing progression. + +## Current checks + +- empty stages +- missing checkpoints +- unassessed concepts +- early capstone placement +- dead-end late stages +- stage-size imbalance +- abrupt prerequisite-load jumps diff --git a/docs/data_models.md b/docs/data_models.md new file mode 100644 index 0000000..25469ab --- /dev/null +++ b/docs/data_models.md @@ -0,0 +1,65 @@ +# Data Model Outline + +## New core entities + +### KnowledgeCandidate +```json +{ + "candidate_id": "kc_001", + "source_type": "learner_export", + "source_artifact_id": 42, + "learner_id": "learner_a", + "pack_id": "stats_intro", + "candidate_kind": "hidden_prerequisite", + "title": "Variance may be an unstated prerequisite for standard deviation", + "summary": "Learner evidence suggests an implicit conceptual dependency.", + "structured_payload": {}, + "evidence_summary": "Repeated low-confidence performance.", + "confidence_hint": 0.72, + "novelty_score": 0.61, + "synthesis_score": 0.58, + "triage_lane": "pack_improvement", + "current_status": "triaged" +} +``` + +### ReviewRecord +```json +{ + "review_id": "rv_001", + "candidate_id": "kc_001", + "reviewer_id": "mentor_1", + "review_kind": "human_review", + "verdict": "accept_pack_improvement", + "rationale": "Supported by learner evidence and pack topology." +} +``` + +### PromotionRecord +```json +{ + "promotion_id": "pr_001", + "candidate_id": "kc_001", + "promotion_target": "pack_improvement", + "target_object_id": "patch_014", + "promotion_status": "approved" +} +``` + +### SynthesisCandidate +```json +{ + "synthesis_id": "syn_001", + "source_concept_id": "entropy_info", + "target_concept_id": "entropy_thermo", + "source_pack_id": "information_theory", + "target_pack_id": "thermodynamics", + "synthesis_kind": "cross_pack_similarity", + "score_total": 0.84, + "score_semantic": 0.88, + "score_structural": 0.71, + "score_trajectory": 0.55, + "score_review_history": 0.60, + "explanation": "These concepts share terminology and play analogous explanatory roles." +} +``` diff --git a/docs/draft-pack-import.md b/docs/draft-pack-import.md new file mode 100644 index 0000000..62d4436 --- /dev/null +++ b/docs/draft-pack-import.md @@ -0,0 +1,36 @@ +# Draft-Pack Import Workflow + +The draft-pack import workflow bridges ingestion output and review workspace setup. + +## Why it exists + +Without import support, users still have to manually: +- locate a generated draft pack +- create a workspace +- copy files into the right directory +- reopen the review tool + +That is exactly the kind of startup friction Didactopus is supposed to reduce. + +## Current scaffold + +This revision adds: +- import API endpoint +- workspace-manager copy/import operation +- UI controls for creating a workspace and importing a draft pack path + +## Import behavior + +The current scaffold: +- creates the target workspace if needed +- copies the source draft-pack directory into `workspace/draft_pack/` +- updates workspace metadata +- allows the workspace to be opened immediately afterward + +## Future work + +- file picker integration +- import validation +- overwrite protection / confirmation +- pack schema validation before import +- duplicate import detection diff --git a/docs/faq.md b/docs/faq.md index c4005a6..89e63ae 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -1,55 +1,19 @@ # FAQ -## Why does Didactopus need ingestion and review tools? +## Why add a workspace manager? -Because useful course material often exists in forms that are difficult to activate for -serious self-directed learning. The issue is not just availability of information; it is -the effort required to transform that information into a usable learning domain. +Because the activation-energy problem is not just parsing content. It is also +staying organized once several candidate domains and draft packs exist. -## What problem is this trying to solve? +## What problem does this solve? -A common problem is the **activation energy hump**: -- the course exists -- the notes exist -- the syllabus exists -- the learner is motivated -- but the path from raw material to usable study structure is still too hard +It reduces the friction of: +- tracking multiple projects +- reopening previous review sessions +- switching among draft packs +- treating Didactopus like an actual working environment -Didactopus is meant to reduce that hump. +## Does this help with online course ingestion? -## Why not just read course webpages directly? - -Because mastery-oriented use needs structure: -- concepts -- prerequisites -- projects -- rubrics -- review decisions -- trust statuses - -Raw course pages do not usually provide these in a directly reusable form. - -## Why have a review UI? - -Because automated ingestion creates drafts, not final trusted packs. A reviewer still needs -to make explicit curation decisions. - -## What can the SPA review UI do in this scaffold? - -- inspect concepts -- edit trust status -- edit notes -- edit prerequisites -- resolve conflicts -- export a promoted reviewed pack - -## Is this already a full production UI? - -No. It is a local-first interactive scaffold with stable data contracts, suitable for -growing into a stronger production interface. - -## Does Didactopus eliminate the need to think? - -No. The goal is to reduce startup friction and organizational overhead, not to replace -judgment. The user or curator still decides what is trustworthy and how the domain should -be shaped. +Yes. One of the barriers to using online course contents is that the setup work +quickly becomes messy. A workspace manager helps turn that mess into a manageable process. diff --git a/docs/full-pack-validation.md b/docs/full-pack-validation.md new file mode 100644 index 0000000..44d2205 --- /dev/null +++ b/docs/full-pack-validation.md @@ -0,0 +1,63 @@ +# Full Pack Validation + +The full pack validator inspects the main Didactopus pack artifacts together. + +## Purpose + +Basic file checks are not enough. A pack can parse successfully while still being +internally inconsistent. + +## Files checked + +- `pack.yaml` +- `concepts.yaml` +- `roadmap.yaml` +- `projects.yaml` +- `rubrics.yaml` + +## Current validation categories + +### File and parse checks +- required files present +- YAML parseable + +### Pack metadata checks +- `name` +- `display_name` +- `version` + +### Concept checks +- concept list exists +- duplicate concept ids +- missing titles +- missing or very thin descriptions + +### Roadmap checks +- stage list exists +- stage concepts refer to known concepts + +### Project checks +- project list exists +- project prerequisite concepts refer to known concepts + +### Rubric checks +- rubric list exists +- each rubric has `id` +- rubric has at least one criterion when present + +## Output + +Validation returns: +- blocking errors +- warnings +- structured summary counts +- import readiness + +## Future work + +- cross-pack dependency validation +- mastery-profile validation +- stronger rubric schema +- semantic duplicate detection +- prerequisite cycle detection +- version compatibility checks diff --git a/docs/graph-prerequisite-analysis.md b/docs/graph-prerequisite-analysis.md new file mode 100644 index 0000000..e291a28 --- /dev/null +++ b/docs/graph-prerequisite-analysis.md @@ -0,0 +1,41 @@ +# Graph-Aware Prerequisite Analysis + +This layer analyzes Didactopus packs as directed graphs over concept dependencies. + +## Purpose + +File validation asks whether a pack parses. +Structural validation asks whether pack artifacts agree. +Semantic QA asks whether a pack looks educationally plausible. +Graph-aware analysis asks whether the concept dependency structure itself looks healthy. + +## Current checks + +### Cycle detection +Flags direct or indirect prerequisite cycles. + +### Isolated concept detection +Flags concepts with no incoming and no outgoing prerequisite edges. + +### Bottleneck detection +Flags concepts with unusually many downstream dependents. + +### Flat-domain heuristic +Flags packs where there are too few prerequisite edges relative to concept count. + +### Deep-chain heuristic +Flags long prerequisite chains that may indicate over-fragmentation. + +## Output + +Returns: +- graph warnings +- summary counts +- structural graph metrics + +## Future work + +- weighted edge confidence +- strongly connected component summaries +- pack-to-pack dependency overlays +- learner-profile-aware path complexity scoring diff --git a/docs/import-validation.md b/docs/import-validation.md new file mode 100644 index 0000000..3cba0c9 --- /dev/null +++ b/docs/import-validation.md @@ -0,0 +1,37 @@ +# Import Validation and Safety + +The import validation layer sits between generated draft packs and managed +review workspaces. + +## Why it exists + +Importing should not be a blind file copy. Users need to know whether a draft +pack appears structurally usable before it is brought into a workspace. + +## Current checks + +The scaffold validates: +- presence of required files +- parseability of `pack.yaml` +- parseability of `concepts.yaml` +- basic pack metadata fields +- concept count +- overwrite risk for target workspace + +## Current outputs + +The preview step returns: +- `ok` +- blocking errors +- warnings +- pack summary +- overwrite warning +- import readiness flag + +## Future work + +- stronger schema validation +- version compatibility checks against Didactopus core +- validation of roadmap/projects/rubrics coherence +- file diff preview when overwriting +- conflict-aware import merge rather than replacement copy diff --git a/docs/license-compliance.md b/docs/license-compliance.md new file mode 100644 index 0000000..f467dcc --- /dev/null +++ b/docs/license-compliance.md @@ -0,0 +1,45 @@ +# Course-Ingestion Compliance Notes + +Didactopus domain packs may be derived from licensed educational sources. +That means the ingestion pipeline should preserve enough information to support: + +- attribution +- license URL retention +- adaptation status +- share-alike / noncommercial flags +- explicit exclusion handling for third-party content +- downstream auditability + +## Recommended source record fields + +Each ingested source should carry: +- source ID +- title +- URL +- publisher +- creator +- license ID +- license URL +- retrieval date +- adaptation flag +- attribution text +- exclusion flag +- exclusion notes + +## Pack-level compliance fields + +A derived pack should carry: +- derived_from_sources +- restrictive_flags +- redistribution_notes +- attribution_required +- share_alike_required +- noncommercial_only + +## MIT OCW-specific pattern + +For MIT OpenCourseWare-derived packs, treat the course material as licensed content while separately recording: +- third-party exclusions +- image/video exceptions +- linked-content exceptions +- any asset not safely covered by the course-level reuse assumption diff --git a/docs/mit-ocw-notes.md b/docs/mit-ocw-notes.md new file mode 100644 index 0000000..9b35b27 --- /dev/null +++ b/docs/mit-ocw-notes.md @@ -0,0 +1,14 @@ +# MIT OpenCourseWare Notes + +MIT OpenCourseWare publishes material under CC BY-NC-SA 4.0 on its terms page, while also warning +that some external or third-party linked content may be excluded from that license. + +That means a Didactopus ingestion pipeline should not simply mark an entire pack as reusable without nuance. + +Recommended handling: +- record MIT OCW course pages as licensed sources +- record individual excluded items explicitly when identified +- preserve the license URL in source metadata +- record whether Didactopus generated an adaptation +- generate an attribution artifact automatically +- propagate a noncommercial/sharealike flag in pack metadata when derived content is redistributed diff --git a/docs/review_and_promotion_workflow.md b/docs/review_and_promotion_workflow.md new file mode 100644 index 0000000..b596f3e --- /dev/null +++ b/docs/review_and_promotion_workflow.md @@ -0,0 +1,332 @@ +# Review-and-Promotion Workflow for Learner-Derived Knowledge + +## Purpose + +Learner-derived knowledge should move through a controlled path from raw +observation to reusable system asset. This workflow is designed to turn exports +into reviewed candidates that can become: + +- accepted pack improvements +- curriculum drafts +- reusable skill bundles +- archived but unadopted suggestions + +## Design goals + +- preserve learner discoveries without assuming correctness +- support reviewer triage and provenance +- separate candidate knowledge from accepted knowledge +- allow multiple promotion targets +- keep enough traceability to understand why a candidate was accepted or rejected + +--- + +## Workflow stages + +### 1. Capture +Input sources include: + +- learner knowledge exports +- mentor observations +- evaluator traces +- synthesis-engine proposals +- artifact-derived observations + +Output: +- one or more **knowledge candidates** + +### 2. Normalize +Convert raw export text and metadata into structured candidate records, such as: + +- concept observation +- hidden prerequisite suggestion +- misconception note +- analogy / cross-pack link suggestion +- curriculum draft fragment +- skill-bundle candidate + +### 3. Triage +Each candidate is routed into a review lane: + +- pack improvement +- curriculum draft +- skill bundle +- archive / backlog + +Triage criteria: +- relevance to existing packs +- novelty +- evidence quality +- reviewer priority +- confidence / ambiguity + +### 4. Review +Human or automated reviewers inspect the candidate. + +Reviewer questions: +- is the claim coherent? +- is it genuinely new or just a restatement? +- does evidence support it? +- does it fit one or more promotion targets? +- what are the risks if promoted? + +### 5. Decision +Possible outcomes: + +- accept into pack improvement queue +- promote to curriculum draft +- promote to skill bundle draft +- archive but keep discoverable +- reject as invalid / duplicate / unsupported + +### 6. Promotion +Accepted items are transformed into target-specific assets: + +- pack patch proposal +- curriculum draft object +- skill bundle object + +### 7. Feedback and provenance +Every decision stores: + +- source export +- source learner +- source pack +- reviewer identity +- rationale +- timestamps +- superseding links if a later decision replaces an earlier one + +--- + +## Target lanes + +## A. Accepted pack improvements + +Typical promoted items: +- missing prerequisite +- poor concept ordering +- missing example +- misleading terminology +- clearer analogy +- cross-pack link worth formalizing + +Output objects: +- patch proposals +- revised concept metadata +- candidate new edges +- explanation replacement suggestions + +Recommended fields: +- pack_id +- concept_ids_affected +- patch_type +- proposed_change +- evidence_summary +- reviewer_notes +- promotion_status + +## B. Curriculum drafts + +Typical promoted items: +- lesson outline +- concept progression plan +- exercise cluster +- misconceptions guide +- capstone prompt +- study guide segment + +Output objects: +- draft lessons +- outline sections +- teacher notes +- question banks + +Recommended fields: +- curriculum_product_type +- topic_focus +- target audience +- prerequisite level +- source concepts +- generated draft +- editorial notes + +## C. Reusable skill bundles + +Typical promoted items: +- concept mastery checklist +- canonical examples +- error patterns +- prerequisite structure +- evaluation rubrics +- recommended actions + +Output objects: +- skill manifest +- skill tests +- skill examples +- operational notes + +Recommended fields: +- skill_name +- target domain +- prerequisites +- expected inputs +- failure modes +- validation checks +- source pack links + +## D. Archived but unadopted suggestions + +Some observations should remain searchable even if not promoted. + +Use this lane when: +- evidence is interesting but incomplete +- idea is plausible but low priority +- reviewer is uncertain +- concept does not fit a current roadmap +- duplication risk exists but insight might still help later + +Recommended fields: +- archive_reason +- potential_future_use +- reviewer_notes +- related packs +- revisit_after + +--- + +## Core data model + +### KnowledgeCandidate +- candidate_id +- source_type +- source_artifact_id +- learner_id +- pack_id +- candidate_kind +- title +- summary +- structured_payload +- evidence_summary +- confidence_hint +- novelty_score +- synthesis_score +- triage_lane +- current_status +- created_at + +### ReviewRecord +- review_id +- candidate_id +- reviewer_id +- review_kind +- verdict +- rationale +- requested_changes +- created_at + +### PromotionRecord +- promotion_id +- candidate_id +- promotion_target +- target_object_id +- promotion_status +- promoted_by +- created_at + +### CandidateLink +- link_id +- candidate_id +- related_candidate_id +- relation_kind +- note + +--- + +## Suggested states + +Candidate states: +- captured +- normalized +- triaged +- under_review +- accepted +- promoted +- archived +- rejected + +Pack improvement states: +- proposed +- approved +- merged +- superseded + +Curriculum draft states: +- draft +- editorial_review +- approved +- published + +Skill bundle states: +- draft +- validation +- approved +- deployed + +--- + +## Promotion rules + +### Pack improvements +Promote when: +- directly improves pack clarity or structure +- supported by evidence or synthesis signal +- low risk of destabilizing pack semantics + +### Curriculum drafts +Promote when: +- pedagogically useful even if not strictly a pack change +- enough material exists to support a lesson, guide, or exercise group + +### Skill bundles +Promote when: +- insight can be operationalized into a reusable structured behavior package +- prerequisites, examples, and evaluation logic are sufficiently clear + +### Archive +Use when: +- the idea is promising but under-evidenced +- better future context may make it valuable +- reviewer wants traceability without immediate adoption + +--- + +## Review UX recommendations + +Reviewer interface should show: + +- candidate summary +- source artifact and export trace +- related concepts and packs +- novelty score +- synthesis score +- suggested promotion targets +- side-by-side comparison with current pack text +- one-click actions for: + - accept as pack improvement + - promote to curriculum draft + - promote to skill bundle + - archive + - reject + +--- + +## Integration with synthesis engine + +Synthesis proposals should enter the same workflow as learner-derived candidates. +This creates a unified promotion pipeline for: + +- human observations +- AI learner observations +- automated synthesis discoveries diff --git a/docs/semantic-qa.md b/docs/semantic-qa.md new file mode 100644 index 0000000..f3e7ef7 --- /dev/null +++ b/docs/semantic-qa.md @@ -0,0 +1,44 @@ +# Semantic QA Layer + +The semantic QA layer sits above structural validation. + +## Purpose + +Structural validation tells us whether a pack is syntactically and referentially +coherent. Semantic QA asks whether it also looks *educationally plausible*. + +## Current checks + +### Near-duplicate title check +Flags concept titles that are lexically very similar. + +### Over-broad concept check +Flags titles that look unusually broad or compound, such as: +- "Prior and Posterior" +- "Statistics and Probability" +- "Modeling and Inference" + +### Description similarity check +Flags concepts whose descriptions appear highly similar. + +### Missing bridge concept check +Looks at successive roadmap stages and flags abrupt jumps where later stages do +not seem to share enough semantic continuity with earlier stages. + +### Thin prerequisite chain check +Flags advanced-sounding concepts that have zero or very few prerequisites. + +## Output + +Semantic QA returns: +- warnings +- summary counts +- finding categories + +## Future work + +- embedding-backed similarity +- prerequisite cycle suspicion scoring +- topic-cluster coherence +- cross-pack semantic overlap +- domain-specific semantic QA plugins diff --git a/docs/synthesis_engine_architecture.md b/docs/synthesis_engine_architecture.md new file mode 100644 index 0000000..9c69f3c --- /dev/null +++ b/docs/synthesis_engine_architecture.md @@ -0,0 +1,291 @@ +# Synthesis Engine Architecture + +## Purpose + +The synthesis engine identifies potentially useful conceptual overlaps across +packs, topics, and learning trajectories. Its goal is to help learners and +maintainers discover connections that improve understanding of the topic of +interest. + +This is not merely a recommendation engine. It is a **cross-domain structural +discovery system**. + +--- + +## Design goals + +- identify meaningful connections across packs +- support analogy, transfer, and hidden-prerequisite discovery +- generate reviewer-friendly candidate proposals +- improve pack quality and curriculum design +- capture surprising learner or AI discoveries +- expose synthesis to users visually and operationally + +--- + +## Kinds of synthesis targets + +### 1. Cross-pack concept similarity +Examples: +- entropy ↔ entropy +- drift ↔ random walk +- selection pressure ↔ optimization pressure + +### 2. Structural analogy +Examples: +- feedback loops in control theory and ecology +- graph search and evolutionary exploration +- signal detection in acoustics and statistical inference + +### 3. Hidden prerequisite discovery +If learners repeatedly fail on a concept despite nominal prerequisites, a +missing dependency may exist. + +### 4. Example transfer +A concept may become easier to understand when illustrated by examples from +another pack. + +### 5. Skill transfer +A skill bundle from one domain may partially apply in another domain. + +--- + +## Data model + +### ConceptNode +- concept_id +- pack_id +- title +- description +- prerequisites +- tags +- examples +- glossary terms +- vector embedding +- graph neighborhood signature + +### SynthesisCandidate +- synthesis_id +- source_concept_id +- target_concept_id +- source_pack_id +- target_pack_id +- synthesis_kind +- score_total +- score_semantic +- score_structural +- score_trajectory +- score_review_history +- explanation +- evidence +- current_status + +### SynthesisCluster +Represents a small group of mutually related concepts across packs. + +Fields: +- cluster_id +- member_concepts +- centroid_embedding +- theme_label +- notes + +### HiddenPrerequisiteCandidate +- source_concept_id +- suspected_missing_prerequisite_id +- signal_strength +- supporting_fail_patterns +- reviewer_status + +--- + +## Scoring methods + +The engine should combine multiple signals. + +### A. Semantic similarity score +Source: +- concept text +- glossary +- examples +- descriptions +- optional embeddings + +Methods: +- cosine similarity on embeddings +- term overlap +- phrase normalization +- ontology-aware synonyms if available + +### B. Structural similarity score +Source: +- prerequisite neighborhoods +- downstream dependencies +- graph motif similarity +- role in pack topology + +Examples: +- concepts that sit in similar graph positions +- concepts that unlock similar kinds of later work + +### C. Learner trajectory score +Source: +- shared error patterns +- similar mastery progression +- evidence timing +- co-improvement patterns across learners + +Examples: +- learners who master A often learn B faster +- failure on X predicts later trouble on Y + +### D. Reviewer history score +Source: +- accepted past synthesis suggestions +- rejected patterns +- reviewer preference patterns + +Use: +- prioritize candidate types with strong track record + +### E. Novelty score +Purpose: +- avoid flooding reviewers with obvious or duplicate links + +Methods: +- de-duplicate against existing pack links +- penalize near-duplicate proposals +- boost under-explored high-signal regions + +--- + +## Composite score + +Suggested first composite: + +score_total = + 0.35 * semantic_similarity + + 0.25 * structural_similarity + + 0.20 * trajectory_signal + + 0.10 * review_prior + + 0.10 * novelty + +This weighting should remain configurable. + +--- + +## Discovery pipeline + +### Step 1. Ingest graph and learner data +Inputs: +- packs +- concepts +- pack metadata +- learner states +- evidence histories +- artifacts +- knowledge exports + +### Step 2. Compute concept features +For each concept: +- embedding +- prerequisite signature +- downstream signature +- learner-error signature +- example signature + +### Step 3. Generate candidate pairs +Possible approaches: +- nearest neighbors in embedding space +- shared tag neighborhoods +- prerequisite motif matches +- frequent learner co-patterns + +### Step 4. Re-rank candidates +Combine semantic, structural, and trajectory scores. + +### Step 5. Group into synthesis clusters +Cluster related candidate pairs into themes such as: +- uncertainty +- feedback +- optimization +- conservation +- branching processes + +### Step 6. Produce explanations +Each candidate should include a compact explanation, for example: +- “These concepts occupy similar prerequisite roles.” +- “Learner error patterns suggest a hidden shared dependency.” +- “Examples in pack A may clarify this concept in pack B.” + +### Step 7. Send to review-and-promotion workflow +All candidates become reviewable objects rather than immediately modifying packs. + +--- + +## Outputs + +The engine should emit candidate objects suitable for promotion into: + +- cross-pack links +- pack improvement suggestions +- curriculum draft notes +- skill-bundle drafts +- archived synthesis notes + +--- + +## UI visualization + +### 1. Synthesis map +Graph overlay showing: +- existing cross-pack links +- proposed synthesis links +- confidence levels +- accepted vs candidate status + +### 2. Candidate explanation panel +For a selected proposed link: +- why it was suggested +- component scores +- source evidence +- similar accepted proposals +- reviewer actions + +### 3. Cluster view +Shows higher-level themes connecting multiple packs. + +### 4. Learner pathway overlay +Allows a maintainer to see where synthesis would help a learner currently stuck in +one pack by borrowing examples or structures from another. + +### 5. Promotion workflow integration +Every synthesis candidate can be: +- accepted as pack improvement +- converted to curriculum draft +- converted to skill bundle +- archived +- rejected + +--- + +## Appropriate uses + +The synthesis engine is especially useful for: + +- interdisciplinary education +- transfer learning support +- AI learner introspection +- pack maintenance +- curriculum design +- discovery of hidden structure + +--- + +## Cautions + +- synthesis suggestions are candidate aids, not guaranteed truths +- semantic similarity alone is not enough +- over-linking can confuse learners +- reviewers need concise explanation and provenance +- accepted synthesis should be visible as intentional structure, not accidental clutter diff --git a/docs/ui_visualization_notes.md b/docs/ui_visualization_notes.md new file mode 100644 index 0000000..8dbd5f9 --- /dev/null +++ b/docs/ui_visualization_notes.md @@ -0,0 +1,33 @@ +# UI Visualization Notes + +## Review workbench +Main panes: + +1. Candidate queue +2. Candidate detail +3. Evidence/provenance panel +4. Promotion actions +5. Related synthesis suggestions + +## Synthesis map +Features: +- zoomable concept supergraph +- accepted vs proposed links +- cross-pack color coding +- cluster highlighting +- filter by score, pack, theme + +## Promotion dashboard +Views: +- pack improvement queue +- curriculum draft queue +- skill bundle queue +- archive browser + +## Learner-facing synthesis hints +The learner view should be selective and helpful, not noisy. + +Good uses: +- “This concept may connect to another pack you know.” +- “An analogy from another topic may help here.” +- “Learners like you often benefit from this bridge concept.” diff --git a/docs/ux-notes.md b/docs/ux-notes.md new file mode 100644 index 0000000..714ccfb --- /dev/null +++ b/docs/ux-notes.md @@ -0,0 +1,34 @@ +# UX Notes for Human Learners + +## First-session design +The first session should: +- fit on one screen when possible +- ask for one meaningful action +- generate one visible success marker + +## Recommended interface pattern +A learner-facing UI should emphasize: +- one main recommendation card +- one secondary reinforcement task +- a small visible mastery map +- a plain-language "why this next?" explanation + +## Fun / rewarding elements +Good candidates: +- concept unlock animations +- progress rings +- milestone badges +- encouraging plain-language summaries +- capstone readiness meter + +Avoid: +- gamification that obscures meaning +- complicated dashboards on first use +- forcing users to interpret opaque confidence math + +## Human-readable learner state +Always expose: +- what changed +- why it changed +- what is still missing +- what the next sensible step is diff --git a/docs/workspace-manager.md b/docs/workspace-manager.md new file mode 100644 index 0000000..475a23f --- /dev/null +++ b/docs/workspace-manager.md @@ -0,0 +1,22 @@ +# Workspace Manager + +The workspace manager provides project-level organization for Didactopus review work. + +## Why it exists + +Without a workspace layer, users still have to manually track: +- which draft packs exist +- where they live +- which one is currently being reviewed +- which ones have promoted outputs + +That creates unnecessary friction. + +## Features in this scaffold + +- workspace registry file +- create workspace +- list workspaces +- open a specific workspace +- track recent workspaces +- expose these through a local bridge API diff --git a/example-pack/concepts.yaml b/example-pack/concepts.yaml new file mode 100644 index 0000000..942666a --- /dev/null +++ b/example-pack/concepts.yaml @@ -0,0 +1,10 @@ +concepts: + - id: prior + title: Prior + prerequisites: [] + - id: posterior + title: Posterior + prerequisites: [prior] + - id: model-checking + title: Model Checking + prerequisites: [posterior] diff --git a/example-pack/pack.yaml b/example-pack/pack.yaml new file mode 100644 index 0000000..8e5db81 --- /dev/null +++ b/example-pack/pack.yaml @@ -0,0 +1,10 @@ +name: bayes-pack +display_name: Bayesian Reasoning +description: Probability, evidence, updating, and model criticism. +audience_level: novice-friendly +first_session_headline: Start with a fast visible win +first_session_body: Read one short orientation, answer one guided question, and leave with your first mastery marker. +first_session_checklist: + - Read the one-screen topic orientation + - Answer one guided exercise + - Write one explanation in your own words diff --git a/example-pack/pack_compliance_manifest.json b/example-pack/pack_compliance_manifest.json new file mode 100644 index 0000000..6a2e6fb --- /dev/null +++ b/example-pack/pack_compliance_manifest.json @@ -0,0 +1,20 @@ +{ + "pack_id": "bayes-pack", + "display_name": "Bayesian Reasoning", + "derived_from_sources": [ + "mit-ocw-bayes", + "excluded-figure" + ], + "attribution_required": true, + "share_alike_required": true, + "noncommercial_only": true, + "restrictive_flags": [ + "share-alike", + "noncommercial", + "excluded-third-party-content" + ], + "redistribution_notes": [ + "Derived redistributable material may need to remain under the same license family.", + "Derived redistributable material may be limited to noncommercial use." + ] +} \ No newline at end of file diff --git a/generated-pack/concepts.yaml b/generated-pack/concepts.yaml new file mode 100644 index 0000000..3a65b82 --- /dev/null +++ b/generated-pack/concepts.yaml @@ -0,0 +1,21 @@ +concepts: + - id: bayes-prior + title: Bayes Prior + description: Prior beliefs before evidence in a probabilistic model. + prerequisites: [] + mastery_signals: + - Explain a prior distribution. + - id: bayes-posterior + title: Bayes Posterior + description: Updated beliefs after evidence in a probabilistic model. + prerequisites: + - bayes-prior + mastery_signals: + - Compare prior and posterior beliefs. + - id: model-checking + title: Model Checking + description: Evaluate whether model assumptions and fit remain plausible. + prerequisites: + - bayes-posterior + mastery_signals: + - Critique a model fit. diff --git a/generated-pack/conflict_report.md b/generated-pack/conflict_report.md new file mode 100644 index 0000000..79ae7db --- /dev/null +++ b/generated-pack/conflict_report.md @@ -0,0 +1,3 @@ +# Conflict Report + +- Example imported conflict. diff --git a/generated-pack/evaluator.yaml b/generated-pack/evaluator.yaml new file mode 100644 index 0000000..bd8de6e --- /dev/null +++ b/generated-pack/evaluator.yaml @@ -0,0 +1,8 @@ +dimensions: + - name: explanation + description: quality of explanation + - name: comparison + description: quality of comparison +evidence_types: + - explanation + - comparison report diff --git a/generated-pack/mastery_ledger.yaml b/generated-pack/mastery_ledger.yaml new file mode 100644 index 0000000..568505a --- /dev/null +++ b/generated-pack/mastery_ledger.yaml @@ -0,0 +1,15 @@ +entry_schema: + concept_id: str + dimension: str + score: float + confidence: float + last_updated: datetime +dimension_mappings: + explanation: explanation + comparison: comparison +evidence_type_mappings: + explanation: text_artifact + comparison report: project_artifact +confidence_update: + method: weighted_average + decay: 0.05 diff --git a/generated-pack/pack.yaml b/generated-pack/pack.yaml new file mode 100644 index 0000000..87fbdd5 --- /dev/null +++ b/generated-pack/pack.yaml @@ -0,0 +1,3 @@ +name: imported-pack +display_name: Imported Pack +version: 0.1.0-draft diff --git a/generated-pack/projects.yaml b/generated-pack/projects.yaml new file mode 100644 index 0000000..4c01d81 --- /dev/null +++ b/generated-pack/projects.yaml @@ -0,0 +1,8 @@ +projects: + - id: compare-beliefs + title: Compare Prior and Posterior + prerequisites: + - bayes-prior + - bayes-posterior + deliverables: + - short report diff --git a/generated-pack/review_report.md b/generated-pack/review_report.md new file mode 100644 index 0000000..d277364 --- /dev/null +++ b/generated-pack/review_report.md @@ -0,0 +1,3 @@ +# Review Report + +- Example imported review flag. diff --git a/generated-pack/roadmap.yaml b/generated-pack/roadmap.yaml new file mode 100644 index 0000000..8b2f6ca --- /dev/null +++ b/generated-pack/roadmap.yaml @@ -0,0 +1,13 @@ +stages: + - id: stage-1 + title: Prior Beliefs + concepts: + - bayes-prior + - id: stage-2 + title: Posterior Updating + concepts: + - bayes-posterior + - id: stage-3 + title: Model Checking + concepts: + - model-checking diff --git a/generated-pack/rubrics.yaml b/generated-pack/rubrics.yaml new file mode 100644 index 0000000..594266b --- /dev/null +++ b/generated-pack/rubrics.yaml @@ -0,0 +1,6 @@ +rubrics: + - id: basic-rubric + title: Basic Rubric + criteria: + - correctness + - explanation diff --git a/pyproject.toml b/pyproject.toml index 57dad6c..d8d63dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "didactopus" version = "0.1.0" -description = "Didactopus: interactive review UI scaffold" +description = "Didactopus: workspace manager for local review UI" readme = "README.md" requires-python = ">=3.10" license = {text = "MIT"} @@ -16,7 +16,7 @@ dependencies = ["pydantic>=2.7", "pyyaml>=6.0"] dev = ["pytest>=8.0", "ruff>=0.6"] [project.scripts] -didactopus-review = "didactopus.main:main" +didactopus-review-bridge = "didactopus.review_bridge_server:main" [tool.setuptools.packages.find] where = ["src"] diff --git a/samples/ATTRIBUTION.md b/samples/ATTRIBUTION.md new file mode 100644 index 0000000..1d2236d --- /dev/null +++ b/samples/ATTRIBUTION.md @@ -0,0 +1,23 @@ +# Attribution + +## Example MIT OpenCourseWare Course Page +- Source ID: mit-ocw-bayes-demo +- URL: https://ocw.mit.edu/courses/example-course/ +- Creator: MIT OpenCourseWare +- Publisher: Massachusetts Institute of Technology +- License: CC BY-NC-SA 4.0 +- License URL: https://creativecommons.org/licenses/by-nc-sa/4.0/ +- Adapted: yes +- Adaptation notes: Didactopus extracted topic structure, concepts, and exercise prompts into a derived domain pack. +- Attribution text: Derived in part from MIT OpenCourseWare material, used under CC BY-NC-SA 4.0. + +## Example Excluded Third-Party Item +- Source ID: mit-ocw-third-party-note +- URL: https://ocw.mit.edu/courses/example-course/pages/lecture-videos/ +- Creator: Third-party rights holder +- Publisher: Massachusetts Institute of Technology +- License: third-party-excluded +- Adapted: no +- Attribution text: Referenced only for exclusion tracking; not reused in redistributed Didactopus artifacts. +- Excluded from upstream course license: yes +- Exclusion notes: This item was flagged as excluded from the OCW Creative Commons license and should not be redistributed as pack content. diff --git a/samples/concepts.yaml b/samples/concepts.yaml new file mode 100644 index 0000000..882d59a --- /dev/null +++ b/samples/concepts.yaml @@ -0,0 +1,10 @@ +concepts: + - id: bayes-prior + title: Bayes Prior + prerequisites: [] + - id: bayes-posterior + title: Bayes Posterior + prerequisites: [bayes-prior] + - id: model-checking + title: Model Checking + prerequisites: [bayes-posterior] diff --git a/samples/learner_state.json b/samples/learner_state.json new file mode 100644 index 0000000..0fd1068 --- /dev/null +++ b/samples/learner_state.json @@ -0,0 +1,14 @@ +{ + "learner_id": "demo-learner", + "records": [ + { + "concept_id": "bayes-prior", + "dimension": "mastery", + "score": 0.81, + "confidence": 0.72, + "evidence_count": 3, + "last_updated": "2026-03-13T12:00:00+00:00" + } + ], + "history": [] +} diff --git a/samples/provenance_manifest.json b/samples/provenance_manifest.json new file mode 100644 index 0000000..1a7893c --- /dev/null +++ b/samples/provenance_manifest.json @@ -0,0 +1,9 @@ +{ + "source_count": 2, + "licenses_present": [ + "CC BY-NC-SA 4.0", + "third-party-excluded" + ], + "excluded_source_count": 1, + "adapted_source_count": 1 +} \ No newline at end of file diff --git a/samples/sources.yaml b/samples/sources.yaml new file mode 100644 index 0000000..0f366b7 --- /dev/null +++ b/samples/sources.yaml @@ -0,0 +1,26 @@ +sources: + - source_id: mit-ocw-bayes + title: Example MIT OpenCourseWare Bayesian Materials + url: https://ocw.mit.edu/courses/example-course/ + publisher: Massachusetts Institute of Technology + creator: MIT OpenCourseWare + license_id: CC BY-NC-SA 4.0 + license_url: https://creativecommons.org/licenses/by-nc-sa/4.0/ + retrieved_at: 2026-03-13 + adapted: true + attribution_text: Derived in part from MIT OpenCourseWare material used under CC BY-NC-SA 4.0. + excluded_from_upstream_license: false + exclusion_notes: "" + + - source_id: excluded-figure + title: Example Third-Party Figure + url: https://ocw.mit.edu/courses/example-course/pages/lecture-videos/ + publisher: Massachusetts Institute of Technology + creator: Third-party rights holder + license_id: third-party-excluded + license_url: "" + retrieved_at: 2026-03-13 + adapted: false + attribution_text: Tracked for exclusion; not reused in redistributed pack content. + excluded_from_upstream_license: true + exclusion_notes: Figure flagged as excluded from the course-level Creative Commons license. diff --git a/src/didactopus/api.py b/src/didactopus/api.py new file mode 100644 index 0000000..1798efa --- /dev/null +++ b/src/didactopus/api.py @@ -0,0 +1,150 @@ +from __future__ import annotations +from fastapi import FastAPI, HTTPException, Header, Depends +from fastapi.middleware.cors import CORSMiddleware +import uvicorn +from .db import Base, engine +from .models import ( + LoginRequest, TokenPair, KnowledgeCandidateCreate, KnowledgeCandidateUpdate, + ReviewCreate, PromoteRequest, SynthesisRunRequest, SynthesisPromoteRequest, + CreateLearnerRequest +) +from .repository import ( + authenticate_user, get_user_by_id, create_learner, learner_owned_by_user, + create_candidate, list_candidates, get_candidate, update_candidate, + create_review, list_reviews, create_promotion, list_promotions, + list_synthesis_candidates, get_synthesis_candidate +) +from .auth import issue_access_token, issue_refresh_token, decode_token, new_token_id +from .synthesis import generate_synthesis_candidates + +Base.metadata.create_all(bind=engine) + +app = FastAPI(title="Didactopus Review Workbench API") +app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"]) + +_refresh_tokens = {} + +def current_user(authorization: str = Header(default="")): + token = authorization.removeprefix("Bearer ").strip() + payload = decode_token(token) if token else None + if not payload or payload.get("kind") != "access": + raise HTTPException(status_code=401, detail="Unauthorized") + user = get_user_by_id(int(payload["sub"])) + if user is None or not user.is_active: + raise HTTPException(status_code=401, detail="Unauthorized") + return user + +def require_reviewer(user = Depends(current_user)): + if user.role not in {"admin", "reviewer"}: + raise HTTPException(status_code=403, detail="Reviewer role required") + return user + +@app.post("/api/login", response_model=TokenPair) +def login(payload: LoginRequest): + user = authenticate_user(payload.username, payload.password) + if user is None: + raise HTTPException(status_code=401, detail="Invalid credentials") + token_id = new_token_id() + _refresh_tokens[token_id] = user.id + return TokenPair( + access_token=issue_access_token(user.id, user.username, user.role), + refresh_token=issue_refresh_token(user.id, user.username, user.role, token_id), + username=user.username, + role=user.role + ) + +@app.post("/api/learners") +def api_create_learner(payload: CreateLearnerRequest, user = Depends(current_user)): + create_learner(user.id, payload.learner_id, payload.display_name) + return {"ok": True, "learner_id": payload.learner_id} + +@app.post("/api/knowledge-candidates") +def api_create_candidate(payload: KnowledgeCandidateCreate, reviewer = Depends(require_reviewer)): + candidate_id = create_candidate(payload) + return {"candidate_id": candidate_id} + +@app.get("/api/knowledge-candidates") +def api_list_candidates(reviewer = Depends(require_reviewer)): + return list_candidates() + +@app.get("/api/knowledge-candidates/{candidate_id}") +def api_get_candidate(candidate_id: int, reviewer = Depends(require_reviewer)): + row = get_candidate(candidate_id) + if row is None: + raise HTTPException(status_code=404, detail="Candidate not found") + return row + +@app.post("/api/knowledge-candidates/{candidate_id}/update") +def api_update_candidate(candidate_id: int, payload: KnowledgeCandidateUpdate, reviewer = Depends(require_reviewer)): + row = update_candidate(candidate_id, triage_lane=payload.triage_lane, current_status=payload.current_status) + if row is None: + raise HTTPException(status_code=404, detail="Candidate not found") + return {"candidate_id": row.id, "triage_lane": row.triage_lane, "current_status": row.current_status} + +@app.post("/api/knowledge-candidates/{candidate_id}/reviews") +def api_create_review(candidate_id: int, payload: ReviewCreate, reviewer = Depends(require_reviewer)): + if get_candidate(candidate_id) is None: + raise HTTPException(status_code=404, detail="Candidate not found") + review_id = create_review(candidate_id, reviewer.id, payload) + return {"review_id": review_id} + +@app.get("/api/knowledge-candidates/{candidate_id}/reviews") +def api_list_reviews(candidate_id: int, reviewer = Depends(require_reviewer)): + return list_reviews(candidate_id) + +@app.post("/api/knowledge-candidates/{candidate_id}/promote") +def api_promote_candidate(candidate_id: int, payload: PromoteRequest, reviewer = Depends(require_reviewer)): + if get_candidate(candidate_id) is None: + raise HTTPException(status_code=404, detail="Candidate not found") + promotion_id = create_promotion(candidate_id, reviewer.id, payload) + return {"promotion_id": promotion_id} + +@app.get("/api/promotions") +def api_list_promotions(reviewer = Depends(require_reviewer)): + return list_promotions() + +@app.post("/api/synthesis/run") +def api_run_synthesis(payload: SynthesisRunRequest, reviewer = Depends(require_reviewer)): + created = generate_synthesis_candidates(payload.source_pack_id, payload.target_pack_id, payload.limit) + return {"created_count": len(created), "synthesis_ids": created} + +@app.get("/api/synthesis/candidates") +def api_list_synthesis(reviewer = Depends(require_reviewer)): + return list_synthesis_candidates() + +@app.get("/api/synthesis/candidates/{synthesis_id}") +def api_get_synthesis(synthesis_id: int, reviewer = Depends(require_reviewer)): + row = get_synthesis_candidate(synthesis_id) + if row is None: + raise HTTPException(status_code=404, detail="Synthesis candidate not found") + return row + +@app.post("/api/synthesis/candidates/{synthesis_id}/promote") +def api_promote_synthesis(synthesis_id: int, payload: SynthesisPromoteRequest, reviewer = Depends(require_reviewer)): + syn = get_synthesis_candidate(synthesis_id) + if syn is None: + raise HTTPException(status_code=404, detail="Synthesis candidate not found") + candidate_id = create_candidate(KnowledgeCandidateCreate( + source_type="synthesis_engine", + source_artifact_id=None, + learner_id="system", + pack_id=syn["source_pack_id"], + candidate_kind="synthesis_proposal", + title=f"Synthesis: {syn['source_concept_id']} ↔ {syn['target_concept_id']}", + summary=syn["explanation"], + structured_payload=syn, + evidence_summary="Promoted from synthesis engine candidate", + confidence_hint=syn["score_total"], + novelty_score=syn["evidence"].get("novelty", 0.0), + synthesis_score=syn["score_total"], + triage_lane=payload.promotion_target, + )) + promotion_id = create_promotion(candidate_id, reviewer.id, PromoteRequest( + promotion_target=payload.promotion_target, + target_object_id="", + promotion_status="approved", + )) + return {"candidate_id": candidate_id, "promotion_id": promotion_id} + +def main(): + uvicorn.run(app, host="127.0.0.1", port=8011) diff --git a/src/didactopus/attribution_builder.py b/src/didactopus/attribution_builder.py new file mode 100644 index 0000000..17e615e --- /dev/null +++ b/src/didactopus/attribution_builder.py @@ -0,0 +1,47 @@ +from __future__ import annotations +from pathlib import Path +import argparse +from .provenance import load_sources, write_provenance_manifest + +def render_attribution_markdown(sources_path: str | Path) -> str: + inventory = load_sources(sources_path) + lines = ["# Attribution", ""] + for src in inventory.sources: + lines.append(f"## {src.title}") + lines.append(f"- Source ID: {src.source_id}") + lines.append(f"- URL: {src.url}") + if src.creator: + lines.append(f"- Creator: {src.creator}") + if src.publisher: + lines.append(f"- Publisher: {src.publisher}") + if src.license_id: + lines.append(f"- License: {src.license_id}") + if src.license_url: + lines.append(f"- License URL: {src.license_url}") + lines.append(f"- Adapted: {'yes' if src.adapted else 'no'}") + if src.adaptation_notes: + lines.append(f"- Adaptation notes: {src.adaptation_notes}") + if src.attribution_text: + lines.append(f"- Attribution text: {src.attribution_text}") + if src.excluded_from_upstream_license: + lines.append(f"- Excluded from upstream course license: yes") + if src.exclusion_notes: + lines.append(f"- Exclusion notes: {src.exclusion_notes}") + lines.append("") + return "\n".join(lines) + +def build_artifacts(sources_path: str | Path, attribution_out: str | Path, manifest_out: str | Path) -> None: + Path(attribution_out).write_text(render_attribution_markdown(sources_path), encoding="utf-8") + inventory = load_sources(sources_path) + write_provenance_manifest(inventory, manifest_out) + +def main() -> None: + parser = argparse.ArgumentParser(description="Build Didactopus attribution artifacts from sources.yaml") + parser.add_argument("sources") + parser.add_argument("--attribution-out", default="ATTRIBUTION.md") + parser.add_argument("--manifest-out", default="provenance_manifest.json") + args = parser.parse_args() + build_artifacts(args.sources, args.attribution_out, args.manifest_out) + +if __name__ == "__main__": + main() diff --git a/src/didactopus/attribution_qa.py b/src/didactopus/attribution_qa.py new file mode 100644 index 0000000..16013b8 --- /dev/null +++ b/src/didactopus/attribution_qa.py @@ -0,0 +1,29 @@ +from __future__ import annotations +from pathlib import Path +from .provenance import load_sources + +def attribution_qa(sources_path: str | Path) -> dict: + inv = load_sources(sources_path) + warnings: list[str] = [] + + for src in inv.sources: + if not src.license_id: + warnings.append(f"Source '{src.source_id}' is missing a license identifier.") + if src.license_id and not src.license_url: + warnings.append(f"Source '{src.source_id}' is missing a license URL.") + if not src.attribution_text: + warnings.append(f"Source '{src.source_id}' is missing attribution text.") + if not src.url: + warnings.append(f"Source '{src.source_id}' is missing a source URL.") + if src.adapted and not src.adaptation_notes: + warnings.append(f"Source '{src.source_id}' is marked adapted but has no adaptation notes.") + if src.excluded_from_upstream_license and not src.exclusion_notes: + warnings.append(f"Source '{src.source_id}' is marked excluded but has no exclusion notes.") + + summary = { + "warning_count": len(warnings), + "source_count": len(inv.sources), + "adapted_source_count": sum(1 for s in inv.sources if s.adapted), + "excluded_source_count": sum(1 for s in inv.sources if s.excluded_from_upstream_license), + } + return {"warnings": warnings, "summary": summary} diff --git a/src/didactopus/auth.py b/src/didactopus/auth.py new file mode 100644 index 0000000..54745ac --- /dev/null +++ b/src/didactopus/auth.py @@ -0,0 +1,35 @@ +from __future__ import annotations +from datetime import datetime, timedelta, timezone +from jose import jwt, JWTError +from passlib.context import CryptContext +import secrets +from .config import load_settings + +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") +settings = load_settings() + +def hash_password(password: str) -> str: + return pwd_context.hash(password) + +def verify_password(password: str, password_hash: str) -> bool: + return pwd_context.verify(password, password_hash) + +def _encode_token(payload: dict, expires_delta: timedelta) -> str: + to_encode = dict(payload) + to_encode["exp"] = datetime.now(timezone.utc) + expires_delta + return jwt.encode(to_encode, settings.jwt_secret, algorithm=settings.jwt_algorithm) + +def issue_access_token(user_id: int, username: str, role: str) -> str: + return _encode_token({"sub": str(user_id), "username": username, "role": role, "kind": "access"}, timedelta(minutes=30)) + +def issue_refresh_token(user_id: int, username: str, role: str, token_id: str) -> str: + return _encode_token({"sub": str(user_id), "username": username, "role": role, "kind": "refresh", "jti": token_id}, timedelta(days=14)) + +def decode_token(token: str) -> dict | None: + try: + return jwt.decode(token, settings.jwt_secret, algorithms=[settings.jwt_algorithm]) + except JWTError: + return None + +def new_token_id() -> str: + return secrets.token_urlsafe(24) diff --git a/src/didactopus/compliance_models.py b/src/didactopus/compliance_models.py new file mode 100644 index 0000000..e26b111 --- /dev/null +++ b/src/didactopus/compliance_models.py @@ -0,0 +1,29 @@ +from __future__ import annotations +from pydantic import BaseModel, Field + +class SourceRecord(BaseModel): + source_id: str + title: str + url: str + publisher: str = "" + creator: str = "" + license_id: str = "" + license_url: str = "" + retrieved_at: str = "" + adapted: bool = False + attribution_text: str = "" + excluded_from_upstream_license: bool = False + exclusion_notes: str = "" + +class PackComplianceManifest(BaseModel): + pack_id: str + display_name: str + derived_from_sources: list[str] = Field(default_factory=list) + attribution_required: bool = True + share_alike_required: bool = False + noncommercial_only: bool = False + restrictive_flags: list[str] = Field(default_factory=list) + redistribution_notes: list[str] = Field(default_factory=list) + +class SourceInventory(BaseModel): + sources: list[SourceRecord] = Field(default_factory=list) diff --git a/src/didactopus/config.py b/src/didactopus/config.py index 01f66f7..c9dfa87 100644 --- a/src/didactopus/config.py +++ b/src/didactopus/config.py @@ -5,13 +5,19 @@ import yaml class ReviewConfig(BaseModel): default_reviewer: str = "Unknown Reviewer" - allow_provisional_concepts: bool = True write_promoted_pack: bool = True - write_review_ledger: bool = True + + +class BridgeConfig(BaseModel): + host: str = "127.0.0.1" + port: int = 8765 + registry_path: str = "workspace_registry.json" + default_workspace_root: str = "workspaces" class AppConfig(BaseModel): review: ReviewConfig = Field(default_factory=ReviewConfig) + bridge: BridgeConfig = Field(default_factory=BridgeConfig) def load_config(path: str | Path) -> AppConfig: diff --git a/src/didactopus/course_ingestion_compliance.py b/src/didactopus/course_ingestion_compliance.py new file mode 100644 index 0000000..3229695 --- /dev/null +++ b/src/didactopus/course_ingestion_compliance.py @@ -0,0 +1,95 @@ +from __future__ import annotations +from pathlib import Path +import argparse, json, yaml +from .compliance_models import SourceInventory, PackComplianceManifest + +def load_sources(path: str | Path) -> SourceInventory: + data = yaml.safe_load(Path(path).read_text(encoding="utf-8")) or {} + return SourceInventory.model_validate(data) + +def build_pack_compliance_manifest( + pack_id: str, + display_name: str, + inventory: SourceInventory, +) -> PackComplianceManifest: + licenses = {s.license_id for s in inventory.sources if s.license_id} + restrictive_flags: list[str] = [] + redistribution_notes: list[str] = [] + + share_alike_required = any("SA" in lic for lic in licenses) + noncommercial_only = any("NC" in lic for lic in licenses) + + if share_alike_required: + restrictive_flags.append("share-alike") + redistribution_notes.append("Derived redistributable material may need to remain under the same license family.") + if noncommercial_only: + restrictive_flags.append("noncommercial") + redistribution_notes.append("Derived redistributable material may be limited to noncommercial use.") + if any(s.excluded_from_upstream_license for s in inventory.sources): + restrictive_flags.append("excluded-third-party-content") + redistribution_notes.append("Some source-linked assets were flagged as excluded from the upstream course license.") + + return PackComplianceManifest( + pack_id=pack_id, + display_name=display_name, + derived_from_sources=[s.source_id for s in inventory.sources], + attribution_required=True, + share_alike_required=share_alike_required, + noncommercial_only=noncommercial_only, + restrictive_flags=restrictive_flags, + redistribution_notes=redistribution_notes, + ) + +def compliance_qa(inventory: SourceInventory, manifest: PackComplianceManifest) -> dict: + warnings: list[str] = [] + + for src in inventory.sources: + if not src.url: + warnings.append(f"Source '{src.source_id}' is missing a URL.") + if not src.license_id: + warnings.append(f"Source '{src.source_id}' is missing a license identifier.") + if src.license_id and not src.license_url: + warnings.append(f"Source '{src.source_id}' is missing a license URL.") + if not src.attribution_text: + warnings.append(f"Source '{src.source_id}' is missing attribution text.") + if src.excluded_from_upstream_license and not src.exclusion_notes: + warnings.append(f"Source '{src.source_id}' is marked excluded but has no exclusion notes.") + + if manifest.attribution_required and not inventory.sources: + warnings.append("Manifest requires attribution but the source inventory is empty.") + + if manifest.share_alike_required and "share-alike" not in manifest.restrictive_flags: + warnings.append("Manifest indicates share-alike but restrictive flags are incomplete.") + + if manifest.noncommercial_only and "noncommercial" not in manifest.restrictive_flags: + warnings.append("Manifest indicates noncommercial-only but restrictive flags are incomplete.") + + return { + "warnings": warnings, + "summary": { + "warning_count": len(warnings), + "source_count": len(inventory.sources), + "share_alike_required": manifest.share_alike_required, + "noncommercial_only": manifest.noncommercial_only, + }, + } + +def write_manifest(manifest: PackComplianceManifest, outpath: str | Path) -> None: + Path(outpath).write_text(json.dumps(manifest.model_dump(), indent=2), encoding="utf-8") + +def main() -> None: + parser = argparse.ArgumentParser(description="Build and QA Didactopus course-ingestion compliance artifacts.") + parser.add_argument("sources") + parser.add_argument("--pack-id", default="demo-pack") + parser.add_argument("--display-name", default="Demo Pack") + parser.add_argument("--out", default="pack_compliance_manifest.json") + args = parser.parse_args() + + inventory = load_sources(args.sources) + manifest = build_pack_compliance_manifest(args.pack_id, args.display_name, inventory) + qa = compliance_qa(inventory, manifest) + write_manifest(manifest, args.out) + print(json.dumps({"manifest": manifest.model_dump(), "qa": qa}, indent=2)) + +if __name__ == "__main__": + main() diff --git a/src/didactopus/coverage_alignment_qa.py b/src/didactopus/coverage_alignment_qa.py new file mode 100644 index 0000000..4a791fe --- /dev/null +++ b/src/didactopus/coverage_alignment_qa.py @@ -0,0 +1,2 @@ +def coverage_alignment_for_pack(source_dir): + return {'warnings': [], 'summary': {'coverage_warning_count': 0}} diff --git a/src/didactopus/db.py b/src/didactopus/db.py new file mode 100644 index 0000000..771e336 --- /dev/null +++ b/src/didactopus/db.py @@ -0,0 +1,8 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import declarative_base, sessionmaker +from .config import load_settings + +settings = load_settings() +engine = create_engine(settings.database_url, future=True) +SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False, future=True) +Base = declarative_base() diff --git a/src/didactopus/demo_run.py b/src/didactopus/demo_run.py new file mode 100644 index 0000000..edafafd --- /dev/null +++ b/src/didactopus/demo_run.py @@ -0,0 +1,47 @@ +from __future__ import annotations +from pathlib import Path +import json, yaml +from .learner_state import LearnerState +from .orchestration_models import LearnerProfile, StopCriteria +from .onboarding import build_initial_run_state, build_first_session_plan +from .orchestrator import run_learning_cycle, apply_demo_evidence + +def load_concepts(path: str | Path) -> list[dict]: + data = yaml.safe_load(Path(path).read_text(encoding="utf-8")) or {} + return list(data.get("concepts", []) or []) + +def main(): + base = Path(__file__).resolve().parents[2] / "samples" + concepts = load_concepts(base / "concepts.yaml") + + profile = LearnerProfile( + learner_id="demo-learner", + display_name="Demo Learner", + target_domain="Bayesian reasoning", + prior_experience="novice", + preferred_session_minutes=20, + motivation_notes="Curious and wants quick visible progress.", + ) + run_state = build_initial_run_state(profile) + plan = build_first_session_plan(profile, concepts) + learner_state = LearnerState(learner_id=profile.learner_id) + + learner_state = apply_demo_evidence(learner_state, "bayes-prior", "2026-03-13T12:00:00+00:00") + + stop = StopCriteria( + min_mastered_concepts=1, + min_average_score=0.70, + min_average_confidence=0.20, + required_capstones=[], + ) + result = run_learning_cycle(learner_state, run_state, concepts, stop) + + payload = { + "first_session_plan": plan.model_dump(), + "cycle_result": result, + "records": [r.model_dump() for r in learner_state.records], + } + print(json.dumps(payload, indent=2)) + +if __name__ == "__main__": + main() diff --git a/src/didactopus/engine.py b/src/didactopus/engine.py new file mode 100644 index 0000000..6b7c236 --- /dev/null +++ b/src/didactopus/engine.py @@ -0,0 +1,110 @@ +from __future__ import annotations +from collections import defaultdict +from .models import LearnerState, PackData + +def concept_depths(pack: PackData) -> dict[str, int]: + concept_map = {c.id: c for c in pack.concepts} + memo = {} + def depth(cid: str) -> int: + if cid in memo: + return memo[cid] + c = concept_map[cid] + if not c.prerequisites: + memo[cid] = 0 + else: + memo[cid] = 1 + max(depth(pid) for pid in c.prerequisites if pid in concept_map) + return memo[cid] + for cid in concept_map: + depth(cid) + return memo + +def stable_layout(pack: PackData, width: int = 900, height: int = 520): + depths = concept_depths(pack) + layers = defaultdict(list) + for c in pack.concepts: + layers[depths.get(c.id, 0)].append(c) + positions = {} + max_depth = max(layers.keys()) if layers else 0 + for d in sorted(layers): + nodes = sorted(layers[d], key=lambda c: c.id) + y = 90 + d * ((height - 160) / max(1, max_depth)) + for idx, node in enumerate(nodes): + if node.position is not None: + positions[node.id] = {"x": node.position.x, "y": node.position.y, "source": "pack_authored"} + else: + spacing = width / (len(nodes) + 1) + x = spacing * (idx + 1) + positions[node.id] = {"x": x, "y": y, "source": "auto_layered"} + return positions + +def prereqs_satisfied(scores: dict[str, float], concept, min_score: float = 0.65) -> bool: + for pid in concept.prerequisites: + if scores.get(pid, 0.0) < min_score: + return False + return True + +def concept_status(scores: dict[str, float], concept, min_score: float = 0.65) -> str: + score = scores.get(concept.id, 0.0) + if score >= min_score: + return "mastered" + if prereqs_satisfied(scores, concept, min_score): + return "active" if score > 0 else "available" + return "locked" + +def build_graph_frames(state: LearnerState, pack: PackData): + concepts = {c.id: c for c in pack.concepts} + layout = stable_layout(pack) + scores = {c.id: 0.0 for c in pack.concepts} + frames = [] + history = sorted(state.history, key=lambda x: x.timestamp) + static_edges = [{"source": pre, "target": c.id, "kind": "prerequisite"} for c in pack.concepts for pre in c.prerequisites] + static_cross = [{ + "source": c.id, + "target_pack_id": link.target_pack_id, + "target_concept_id": link.target_concept_id, + "relationship": link.relationship, + "kind": "cross_pack" + } for c in pack.concepts for link in c.cross_pack_links] + for idx, ev in enumerate(history): + if ev.concept_id in scores: + scores[ev.concept_id] = ev.score + nodes = [] + for cid, concept in concepts.items(): + score = scores.get(cid, 0.0) + status = concept_status(scores, concept) + pos = layout[cid] + nodes.append({ + "id": cid, + "title": concept.title, + "score": score, + "status": status, + "size": 20 + int(score * 30), + "x": pos["x"], + "y": pos["y"], + "layout_source": pos["source"], + }) + frames.append({ + "index": idx, + "timestamp": ev.timestamp, + "event_kind": ev.kind, + "focus_concept_id": ev.concept_id, + "nodes": nodes, + "edges": static_edges, + "cross_pack_links": static_cross, + }) + if not frames: + nodes = [] + for c in pack.concepts: + pos = layout[c.id] + nodes.append({ + "id": c.id, + "title": c.title, + "score": 0.0, + "status": "available" if not c.prerequisites else "locked", + "size": 20, + "x": pos["x"], + "y": pos["y"], + "layout_source": pos["source"], + }) + frames.append({"index": 0, "timestamp": "", "event_kind": "empty", "focus_concept_id": "", "nodes": nodes, "edges": static_edges, "cross_pack_links": static_cross}) + return frames diff --git a/src/didactopus/evaluator_alignment_qa.py b/src/didactopus/evaluator_alignment_qa.py new file mode 100644 index 0000000..e7ddb25 --- /dev/null +++ b/src/didactopus/evaluator_alignment_qa.py @@ -0,0 +1,2 @@ +def evaluator_alignment_for_pack(source_dir): + return {'warnings': [], 'summary': {'evaluator_warning_count': 0}} diff --git a/src/didactopus/evidence_flow_ledger_qa.py b/src/didactopus/evidence_flow_ledger_qa.py new file mode 100644 index 0000000..3e25815 --- /dev/null +++ b/src/didactopus/evidence_flow_ledger_qa.py @@ -0,0 +1,76 @@ +import re +from .pack_validator import load_pack_artifacts + +def tok(text: str) -> set[str]: + return {t for t in re.sub(r"[^a-z0-9]+", " ", str(text).lower()).split() if t} + +def evidence_flow_ledger_for_pack(source_dir): + loaded = load_pack_artifacts(source_dir) + if not loaded["ok"]: + return {"warnings": [], "summary": {"ledger_warning_count": 0}} + + arts = loaded["artifacts"] + concepts = arts["concepts"].get("concepts", []) or [] + roadmap = arts["roadmap"].get("stages", []) or [] + projects = arts["projects"].get("projects", []) or [] + evaluator = arts["evaluator"] or {} + ledger = arts["mastery_ledger"] or {} + + dimensions = evaluator.get("dimensions", []) or [] + evidence_types = evaluator.get("evidence_types", []) or [] + dimension_mappings = ledger.get("dimension_mappings", {}) or {} + evidence_mappings = ledger.get("evidence_type_mappings", {}) or {} + entry_schema = ledger.get("entry_schema", {}) or {} + confidence_update = ledger.get("confidence_update", {}) or {} + + warnings = [] + + dim_names = [d if isinstance(d, str) else d.get("name", "") for d in dimensions] + ev_names = [e if isinstance(e, str) else e.get("name", "") for e in evidence_types] + + for name in dim_names: + if name and name not in dimension_mappings: + warnings.append(f"Evaluator dimension '{name}' has no mastery-ledger mapping.") + + for name in ev_names: + if name and name not in evidence_mappings: + warnings.append(f"Evidence type '{name}' has no mastery-ledger entry mapping.") + + route_tokens = set().union(*[tok(x) for x in (dim_names + ev_names)]) if (dim_names or ev_names) else set() + signal_count = 0 + unrouted_signal_count = 0 + for concept in concepts: + for signal in concept.get("mastery_signals", []) or []: + signal_count += 1 + st = tok(signal) + if st and len(st & route_tokens) == 0: + unrouted_signal_count += 1 + warnings.append(f"Mastery signal for concept '{concept.get('id')}' has no visible ledger update path via evaluator dimensions or evidence types.") + + checkpoint_tokens = tok(" ".join(str(i) for s in roadmap for i in (s.get("checkpoint", []) or []))) + deliverable_tokens = tok(" ".join(str(i) for p in projects for i in (p.get("deliverables", []) or []))) + evidence_tokens = set().union(*[tok(x) for x in ev_names]) if ev_names else set() + + if checkpoint_tokens and evidence_tokens and len(checkpoint_tokens & evidence_tokens) == 0: + warnings.append("Checkpoint activity has no clear evidence-flow support from declared evidence types.") + if deliverable_tokens and evidence_tokens and len(deliverable_tokens & evidence_tokens) == 0: + warnings.append("Project deliverables have no clear evidence-flow support from declared evidence types.") + + required = {"concept_id", "dimension", "score", "confidence", "last_updated"} + missing = sorted(required - set(entry_schema.keys())) + if missing: + warnings.append("Mastery-ledger entry schema is missing required fields: " + ", ".join(missing)) + if "confidence" not in entry_schema or not confidence_update: + warnings.append("Repeated assessment support for confidence updates appears to be missing.") + + return { + "warnings": warnings, + "summary": { + "ledger_warning_count": len(warnings), + "dimension_count": len(dim_names), + "evidence_type_count": len(ev_names), + "mastery_signal_count": signal_count, + "unrouted_mastery_signal_count": unrouted_signal_count, + "ledger_schema_field_count": len(entry_schema), + }, + } diff --git a/src/didactopus/export_svg.py b/src/didactopus/export_svg.py new file mode 100644 index 0000000..1bb04a0 --- /dev/null +++ b/src/didactopus/export_svg.py @@ -0,0 +1,39 @@ +from __future__ import annotations +import json +from pathlib import Path + +def color_for_status(status: str) -> str: + return { + "mastered": "#1f7a1f", + "active": "#2d6cdf", + "available": "#c48a00", + "locked": "#9aa4b2", + }.get(status, "#9aa4b2") + +def frame_to_svg(frame: dict, width: int = 960, height: int = 560) -> str: + parts = [f''] + parts.append('') + for edge in frame.get("edges", []): + src = next((n for n in frame["nodes"] if n["id"] == edge["source"]), None) + dst = next((n for n in frame["nodes"] if n["id"] == edge["target"]), None) + if src and dst: + parts.append(f'') + for edge in frame.get("cross_pack_links", []): + src = next((n for n in frame["nodes"] if n["id"] == edge["source"]), None) + if src: + parts.append(f'') + for node in frame.get("nodes", []): + fill = color_for_status(node["status"]) + parts.append(f'') + parts.append(f'{node["title"]}') + parts.append(f'{node["score"]:.2f} · {node["status"]}') + parts.append("") + return "".join(parts) + +def export_svg_frames(payload_path: str, out_dir: str): + payload = json.loads(Path(payload_path).read_text(encoding="utf-8")) + out = Path(out_dir) + out.mkdir(parents=True, exist_ok=True) + for frame in payload.get("frames", []): + svg = frame_to_svg(frame) + (out / f'frame_{frame["index"]:04d}.svg').write_text(svg, encoding="utf-8") diff --git a/src/didactopus/graph_qa.py b/src/didactopus/graph_qa.py new file mode 100644 index 0000000..eed1bf0 --- /dev/null +++ b/src/didactopus/graph_qa.py @@ -0,0 +1,91 @@ +from __future__ import annotations +from collections import defaultdict, deque +from .pack_validator import load_pack_artifacts + +def graph_qa_for_pack(source_dir) -> dict: + loaded = load_pack_artifacts(source_dir) + if not loaded["ok"]: + return {"warnings": [], "summary": {"graph_warning_count": 0}} + + concepts = loaded["artifacts"]["concepts"].get("concepts", []) or [] + concept_ids = [c.get("id") for c in concepts if c.get("id")] + prereqs = {c.get("id"): list(c.get("prerequisites", []) or []) for c in concepts if c.get("id")} + + incoming = defaultdict(set) + outgoing = defaultdict(set) + for cid, pres in prereqs.items(): + for p in pres: + outgoing[p].add(cid) + incoming[cid].add(p) + + warnings = [] + + # Cycle detection + WHITE, GRAY, BLACK = 0, 1, 2 + color = {cid: WHITE for cid in concept_ids} + stack = [] + found_cycles = [] + + def dfs(node): + color[node] = GRAY + stack.append(node) + for nxt in outgoing.get(node, []): + if color.get(nxt, WHITE) == WHITE: + dfs(nxt) + elif color.get(nxt) == GRAY: + if nxt in stack: + idx = stack.index(nxt) + found_cycles.append(stack[idx:] + [nxt]) + stack.pop() + color[node] = BLACK + + for cid in concept_ids: + if color[cid] == WHITE: + dfs(cid) + + for cyc in found_cycles: + warnings.append("Prerequisite cycle detected: " + " -> ".join(cyc)) + + # Isolated concepts + for cid in concept_ids: + if len(incoming[cid]) == 0 and len(outgoing[cid]) == 0: + warnings.append(f"Concept '{cid}' is isolated from the prerequisite graph.") + + # Bottlenecks + threshold = 3 + for cid in concept_ids: + if len(outgoing[cid]) >= threshold: + warnings.append(f"Concept '{cid}' is a bottleneck with {len(outgoing[cid])} downstream dependents.") + + # Flatness + edge_count = sum(len(v) for v in prereqs.values()) + if len(concept_ids) >= 4 and edge_count <= max(1, len(concept_ids) // 4): + warnings.append("Pack appears suspiciously flat: very few prerequisite edges relative to concept count.") + + # Deep chains + indegree = {cid: len(incoming[cid]) for cid in concept_ids} + q = deque([cid for cid in concept_ids if indegree[cid] == 0]) + longest = {cid: 1 for cid in concept_ids} + visited = 0 + while q: + node = q.popleft() + visited += 1 + for nxt in outgoing.get(node, []): + longest[nxt] = max(longest.get(nxt, 1), longest[node] + 1) + indegree[nxt] -= 1 + if indegree[nxt] == 0: + q.append(nxt) + + max_chain = max(longest.values()) if longest else 0 + if max_chain >= 6: + warnings.append(f"Pack has a deep prerequisite chain of length {max_chain}, which may indicate over-fragmentation.") + + summary = { + "graph_warning_count": len(warnings), + "concept_count": len(concept_ids), + "edge_count": edge_count, + "max_chain_length": max_chain, + "cycle_count": len(found_cycles), + "isolated_count": sum(1 for cid in concept_ids if len(incoming[cid]) == 0 and len(outgoing[cid]) == 0), + } + return {"warnings": warnings, "summary": summary} diff --git a/src/didactopus/import_validator.py b/src/didactopus/import_validator.py new file mode 100644 index 0000000..8bde4f5 --- /dev/null +++ b/src/didactopus/import_validator.py @@ -0,0 +1,20 @@ +from __future__ import annotations +from pathlib import Path +from .review_schema import ImportPreview +from .pack_validator import validate_pack_directory +from .semantic_qa import semantic_qa_for_pack + +def preview_draft_pack_import(source_dir: str | Path, workspace_id: str, overwrite_required: bool = False) -> ImportPreview: + result = validate_pack_directory(source_dir) + semantic = semantic_qa_for_pack(source_dir) if result["ok"] else {"warnings": [], "summary": {}} + preview = ImportPreview( + source_dir=str(Path(source_dir)), + workspace_id=workspace_id, + overwrite_required=overwrite_required, + ok=result["ok"], + errors=list(result["errors"]), + warnings=list(result["warnings"]), + summary=dict(result["summary"]), + semantic_warnings=list(semantic["warnings"]), + ) + return preview diff --git a/src/didactopus/knowledge_export.py b/src/didactopus/knowledge_export.py new file mode 100644 index 0000000..1679692 --- /dev/null +++ b/src/didactopus/knowledge_export.py @@ -0,0 +1,59 @@ +from __future__ import annotations +import json +from pathlib import Path +from .repository import get_pack, load_learner_state + +def build_knowledge_snapshot(learner_id: str, pack_id: str) -> dict: + pack = get_pack(pack_id) + state = load_learner_state(learner_id) + concept_map = {c.id: c for c in (pack.concepts if pack else [])} + observations = [] + for rec in state.records: + concept = concept_map.get(rec.concept_id) + observations.append({ + "concept_id": rec.concept_id, + "title": concept.title if concept else rec.concept_id, + "score": rec.score, + "confidence": rec.confidence, + "evidence_count": rec.evidence_count, + "interpretation": ( + "candidate mastery" + if rec.score >= 0.65 and rec.confidence >= 0.5 + else "still developing" + ), + }) + return { + "learner_id": learner_id, + "pack_id": pack_id, + "export_kind": "knowledge_snapshot", + "pack_title": pack.title if pack else "", + "observations": observations, + "pack_improvement_candidates": [ + "Look for concepts with repeated low-confidence evidence.", + "Check whether hidden prerequisites are missing.", + "Inspect surprising learner success for better examples or shortcuts." + ], + "curriculum_product_candidates": [ + "study_guide", + "lesson_outline", + "exercise_set", + "mentor_notes" + ], + "skill_export_candidates": [ + "agent skill manifest", + "evaluation checklist", + "failure-mode notes", + "canonical examples" + ], + } + +def main(): + import argparse + parser = argparse.ArgumentParser() + parser.add_argument("learner_id") + parser.add_argument("pack_id") + parser.add_argument("out_json") + args = parser.parse_args() + payload = build_knowledge_snapshot(args.learner_id, args.pack_id) + Path(args.out_json).write_text(json.dumps(payload, indent=2), encoding="utf-8") + print(f"Wrote {args.out_json}") diff --git a/src/didactopus/learner_state.py b/src/didactopus/learner_state.py new file mode 100644 index 0000000..4b301ca --- /dev/null +++ b/src/didactopus/learner_state.py @@ -0,0 +1,33 @@ +from __future__ import annotations +from pydantic import BaseModel, Field +from typing import Literal + +EvidenceKind = Literal["checkpoint", "project", "exercise", "review"] + +class MasteryRecord(BaseModel): + concept_id: str + dimension: str + score: float = 0.0 + confidence: float = 0.0 + evidence_count: int = 0 + last_updated: str = "" + +class EvidenceEvent(BaseModel): + concept_id: str + dimension: str + score: float + confidence_hint: float = 0.5 + timestamp: str + kind: EvidenceKind = "exercise" + source_id: str = "" + +class LearnerState(BaseModel): + learner_id: str + records: list[MasteryRecord] = Field(default_factory=list) + history: list[EvidenceEvent] = Field(default_factory=list) + + def get_record(self, concept_id: str, dimension: str) -> MasteryRecord | None: + for rec in self.records: + if rec.concept_id == concept_id and rec.dimension == dimension: + return rec + return None diff --git a/src/didactopus/models.py b/src/didactopus/models.py new file mode 100644 index 0000000..16f12be --- /dev/null +++ b/src/didactopus/models.py @@ -0,0 +1,67 @@ +from __future__ import annotations +from pydantic import BaseModel, Field + +class LoginRequest(BaseModel): + username: str + password: str + +class TokenPair(BaseModel): + access_token: str + refresh_token: str + token_type: str = "bearer" + username: str + role: str + +class KnowledgeCandidateCreate(BaseModel): + source_type: str = "learner_export" + source_artifact_id: int | None = None + learner_id: str + pack_id: str + candidate_kind: str + title: str + summary: str = "" + structured_payload: dict = Field(default_factory=dict) + evidence_summary: str = "" + confidence_hint: float = 0.0 + novelty_score: float = 0.0 + synthesis_score: float = 0.0 + triage_lane: str = "archive" + +class KnowledgeCandidateUpdate(BaseModel): + triage_lane: str | None = None + current_status: str | None = None + +class ReviewCreate(BaseModel): + review_kind: str = "human_review" + verdict: str + rationale: str = "" + requested_changes: str = "" + +class PromoteRequest(BaseModel): + promotion_target: str + target_object_id: str = "" + promotion_status: str = "approved" + +class SynthesisRunRequest(BaseModel): + source_pack_id: str | None = None + target_pack_id: str | None = None + limit: int = 20 + +class SynthesisPromoteRequest(BaseModel): + promotion_target: str = "pack_improvement" + +class CreateLearnerRequest(BaseModel): + learner_id: str + display_name: str = "" + +class MasteryRecord(BaseModel): + concept_id: str + dimension: str + score: float = 0.0 + confidence: float = 0.0 + evidence_count: int = 0 + last_updated: str = "" + +class LearnerState(BaseModel): + learner_id: str + records: list[MasteryRecord] = Field(default_factory=list) diff --git a/src/didactopus/onboarding.py b/src/didactopus/onboarding.py new file mode 100644 index 0000000..22f14c6 --- /dev/null +++ b/src/didactopus/onboarding.py @@ -0,0 +1,20 @@ +from __future__ import annotations +from .orchestration_models import LearnerProfile, SessionPlan, RunState + +def build_initial_run_state(profile: LearnerProfile) -> RunState: + return RunState(profile=profile, status="onboarding") + +def build_first_session_plan(profile: LearnerProfile, concepts: list[dict]) -> SessionPlan: + first_title = concepts[0].get("title", "Foundations") if concepts else "Foundations" + return SessionPlan( + headline=f"Start with {first_title}", + next_action=f"Read the short orientation, then attempt one guided exercise on {first_title}.", + why_now="Early success lowers activation energy and gives the learner a clear entry point.", + estimated_minutes=min(max(profile.preferred_session_minutes, 15), 35), + tasks=[ + "Read the one-screen topic orientation", + f"Try one guided exercise on {first_title}", + "Write a short explanation in your own words", + ], + reward_note="You should finish this session with a visible first mastery marker.", + ) diff --git a/src/didactopus/orchestration_models.py b/src/didactopus/orchestration_models.py new file mode 100644 index 0000000..26c2c38 --- /dev/null +++ b/src/didactopus/orchestration_models.py @@ -0,0 +1,39 @@ +from __future__ import annotations +from pydantic import BaseModel, Field +from typing import Literal + +RunStatus = Literal["not_started", "onboarding", "active", "blocked", "ready_for_capstone", "claimed_expertise"] + +class LearnerProfile(BaseModel): + learner_id: str + display_name: str = "" + target_domain: str = "" + prior_experience: str = "unknown" + preferred_session_minutes: int = 25 + motivation_notes: str = "" + wants_fun_feedback: bool = True + wants_concise_guidance: bool = True + +class SessionPlan(BaseModel): + headline: str + next_action: str + why_now: str + estimated_minutes: int = 20 + tasks: list[str] = Field(default_factory=list) + reward_note: str = "" + +class StopCriteria(BaseModel): + min_mastered_concepts: int = 0 + min_average_score: float = 0.75 + min_average_confidence: float = 0.60 + required_capstones: list[str] = Field(default_factory=list) + +class RunState(BaseModel): + profile: LearnerProfile + status: RunStatus = "not_started" + unlocked_concepts: list[str] = Field(default_factory=list) + active_concepts: list[str] = Field(default_factory=list) + blocked_concepts: list[str] = Field(default_factory=list) + completed_projects: list[str] = Field(default_factory=list) + completed_capstones: list[str] = Field(default_factory=list) + milestone_log: list[str] = Field(default_factory=list) diff --git a/src/didactopus/orchestration_notes.md b/src/didactopus/orchestration_notes.md new file mode 100644 index 0000000..b0f58f2 --- /dev/null +++ b/src/didactopus/orchestration_notes.md @@ -0,0 +1,3 @@ +This repository snapshot keeps the Python scaffolding simple while pushing the learner-facing +experience into a runnable UI prototype. A later implementation can expose these Python models +through a FastAPI or similar API. diff --git a/src/didactopus/orchestrator.py b/src/didactopus/orchestrator.py new file mode 100644 index 0000000..e17a147 --- /dev/null +++ b/src/didactopus/orchestrator.py @@ -0,0 +1,47 @@ +from __future__ import annotations +from .learner_state import LearnerState, EvidenceEvent +from .orchestration_models import RunState, SessionPlan, StopCriteria +from .recommendations import recommend_next_concepts, recommend_reinforcement_targets +from .progression_engine import apply_evidence +from .stop_criteria import assess_claim_readiness +from .ux_feedback import format_recommendation_card, format_reward_message + +def run_learning_cycle( + learner_state: LearnerState, + run_state: RunState, + concepts: list[dict], + stop_criteria: StopCriteria, +) -> dict: + next_concepts = recommend_next_concepts(learner_state, concepts) + reinforcement = recommend_reinforcement_targets(learner_state) + cards = [format_recommendation_card(item) for item in next_concepts[:3]] + + readiness = assess_claim_readiness(learner_state, run_state, stop_criteria) + if readiness["ready"]: + run_state.status = "claimed_expertise" + run_state.milestone_log.append(format_reward_message("milestone", "usable expertise threshold met")) + elif next_concepts: + run_state.status = "active" + elif reinforcement: + run_state.status = "active" + else: + run_state.status = "blocked" + + return { + "run_status": run_state.status, + "recommendation_cards": cards, + "reinforcement_targets": reinforcement[:5], + "claim_readiness": readiness, + } + +def apply_demo_evidence(learner_state: LearnerState, concept_id: str, timestamp: str) -> LearnerState: + event = EvidenceEvent( + concept_id=concept_id, + dimension="mastery", + score=0.82, + confidence_hint=0.75, + timestamp=timestamp, + kind="checkpoint", + source_id=f"demo-{concept_id}", + ) + return apply_evidence(learner_state, event) diff --git a/src/didactopus/orm.py b/src/didactopus/orm.py new file mode 100644 index 0000000..668611f --- /dev/null +++ b/src/didactopus/orm.py @@ -0,0 +1,97 @@ +from sqlalchemy import String, Integer, Float, ForeignKey, Text, Boolean +from sqlalchemy.orm import Mapped, mapped_column +from .db import Base + +class UserORM(Base): + __tablename__ = "users" + id: Mapped[int] = mapped_column(Integer, primary_key=True) + username: Mapped[str] = mapped_column(String(100), unique=True, index=True) + password_hash: Mapped[str] = mapped_column(String(255)) + role: Mapped[str] = mapped_column(String(50), default="learner") + is_active: Mapped[bool] = mapped_column(Boolean, default=True) + +class PackORM(Base): + __tablename__ = "packs" + id: Mapped[str] = mapped_column(String(100), primary_key=True) + owner_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True) + policy_lane: Mapped[str] = mapped_column(String(50), default="personal") + title: Mapped[str] = mapped_column(String(255)) + subtitle: Mapped[str] = mapped_column(Text, default="") + level: Mapped[str] = mapped_column(String(100), default="novice-friendly") + data_json: Mapped[str] = mapped_column(Text) + is_published: Mapped[bool] = mapped_column(Boolean, default=False) + +class LearnerORM(Base): + __tablename__ = "learners" + id: Mapped[str] = mapped_column(String(100), primary_key=True) + owner_user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), index=True) + display_name: Mapped[str] = mapped_column(String(255), default="") + +class MasteryRecordORM(Base): + __tablename__ = "mastery_records" + id: Mapped[int] = mapped_column(Integer, primary_key=True) + learner_id: Mapped[str] = mapped_column(ForeignKey("learners.id"), index=True) + concept_id: Mapped[str] = mapped_column(String(100), index=True) + dimension: Mapped[str] = mapped_column(String(100), default="mastery") + score: Mapped[float] = mapped_column(Float, default=0.0) + confidence: Mapped[float] = mapped_column(Float, default=0.0) + evidence_count: Mapped[int] = mapped_column(Integer, default=0) + last_updated: Mapped[str] = mapped_column(String(100), default="") + +class KnowledgeCandidateORM(Base): + __tablename__ = "knowledge_candidates" + id: Mapped[int] = mapped_column(Integer, primary_key=True) + source_type: Mapped[str] = mapped_column(String(50), default="learner_export") + source_artifact_id: Mapped[int | None] = mapped_column(Integer, nullable=True) + learner_id: Mapped[str] = mapped_column(String(100), index=True) + pack_id: Mapped[str] = mapped_column(String(100), index=True) + candidate_kind: Mapped[str] = mapped_column(String(100), index=True) + title: Mapped[str] = mapped_column(String(255)) + summary: Mapped[str] = mapped_column(Text, default="") + structured_payload_json: Mapped[str] = mapped_column(Text, default="{}") + evidence_summary: Mapped[str] = mapped_column(Text, default="") + confidence_hint: Mapped[float] = mapped_column(Float, default=0.0) + novelty_score: Mapped[float] = mapped_column(Float, default=0.0) + synthesis_score: Mapped[float] = mapped_column(Float, default=0.0) + triage_lane: Mapped[str] = mapped_column(String(50), default="archive") + current_status: Mapped[str] = mapped_column(String(50), default="captured") + created_at: Mapped[str] = mapped_column(String(100), default="") + +class ReviewRecordORM(Base): + __tablename__ = "review_records" + id: Mapped[int] = mapped_column(Integer, primary_key=True) + candidate_id: Mapped[int] = mapped_column(ForeignKey("knowledge_candidates.id"), index=True) + reviewer_id: Mapped[int] = mapped_column(ForeignKey("users.id"), index=True) + review_kind: Mapped[str] = mapped_column(String(50), default="human_review") + verdict: Mapped[str] = mapped_column(String(100), default="") + rationale: Mapped[str] = mapped_column(Text, default="") + requested_changes: Mapped[str] = mapped_column(Text, default="") + created_at: Mapped[str] = mapped_column(String(100), default="") + +class PromotionRecordORM(Base): + __tablename__ = "promotion_records" + id: Mapped[int] = mapped_column(Integer, primary_key=True) + candidate_id: Mapped[int] = mapped_column(ForeignKey("knowledge_candidates.id"), index=True) + promotion_target: Mapped[str] = mapped_column(String(50), index=True) + target_object_id: Mapped[str] = mapped_column(String(100), default="") + promotion_status: Mapped[str] = mapped_column(String(50), default="draft") + promoted_by: Mapped[int] = mapped_column(ForeignKey("users.id"), index=True) + created_at: Mapped[str] = mapped_column(String(100), default="") + +class SynthesisCandidateORM(Base): + __tablename__ = "synthesis_candidates" + id: Mapped[int] = mapped_column(Integer, primary_key=True) + source_concept_id: Mapped[str] = mapped_column(String(100), index=True) + target_concept_id: Mapped[str] = mapped_column(String(100), index=True) + source_pack_id: Mapped[str] = mapped_column(String(100), index=True) + target_pack_id: Mapped[str] = mapped_column(String(100), index=True) + synthesis_kind: Mapped[str] = mapped_column(String(100), default="cross_pack_similarity") + score_total: Mapped[float] = mapped_column(Float, default=0.0) + score_semantic: Mapped[float] = mapped_column(Float, default=0.0) + score_structural: Mapped[float] = mapped_column(Float, default=0.0) + score_trajectory: Mapped[float] = mapped_column(Float, default=0.0) + score_review_history: Mapped[float] = mapped_column(Float, default=0.0) + explanation: Mapped[str] = mapped_column(Text, default="") + evidence_json: Mapped[str] = mapped_column(Text, default="{}") + current_status: Mapped[str] = mapped_column(String(50), default="proposed") + created_at: Mapped[str] = mapped_column(String(100), default="") diff --git a/src/didactopus/pack_to_frontend.py b/src/didactopus/pack_to_frontend.py new file mode 100644 index 0000000..4d3b14b --- /dev/null +++ b/src/didactopus/pack_to_frontend.py @@ -0,0 +1,60 @@ +from __future__ import annotations +from pathlib import Path +import argparse, json, yaml + +def load_yaml(path: Path) -> dict: + return yaml.safe_load(path.read_text(encoding="utf-8")) or {} + +def convert_pack(pack_dir: str | Path) -> dict: + pack_dir = Path(pack_dir) + pack = load_yaml(pack_dir / "pack.yaml") + concepts_data = load_yaml(pack_dir / "concepts.yaml") + compliance_path = pack_dir / "pack_compliance_manifest.json" + compliance = json.loads(compliance_path.read_text(encoding="utf-8")) if compliance_path.exists() else {} + + concepts = [] + for item in concepts_data.get("concepts", []) or []: + concepts.append({ + "id": item.get("id"), + "title": item.get("title", item.get("id", "")), + "prerequisites": list(item.get("prerequisites", []) or []), + "masteryDimension": "mastery", + "exerciseReward": f"{item.get('title', item.get('id', 'Concept'))} progress recorded", + }) + + onboarding = { + "headline": pack.get("first_session_headline", f"Start with {concepts[0]['title']}" if concepts else "Start here"), + "body": pack.get("first_session_body", "Begin with one short activity and get a visible mastery marker."), + "checklist": list(pack.get("first_session_checklist", [ + "Read the short orientation", + "Try one guided exercise", + "Write one explanation in your own words", + ])), + } + + return { + "id": pack.get("name", pack_dir.name), + "title": pack.get("display_name", pack.get("name", pack_dir.name)), + "subtitle": pack.get("description", ""), + "level": pack.get("audience_level", "novice-friendly"), + "concepts": concepts, + "onboarding": onboarding, + "compliance": { + "sources": len(compliance.get("derived_from_sources", []) or []), + "attributionRequired": bool(compliance.get("attribution_required", False)), + "shareAlikeRequired": bool(compliance.get("share_alike_required", False)), + "noncommercialOnly": bool(compliance.get("noncommercial_only", False)), + "flags": list(compliance.get("restrictive_flags", []) or []), + }, + } + +def main() -> None: + parser = argparse.ArgumentParser(description="Convert a Didactopus pack directory to learner-frontend JSON.") + parser.add_argument("pack_dir") + parser.add_argument("--out", default="pack.frontend.json") + args = parser.parse_args() + payload = convert_pack(args.pack_dir) + Path(args.out).write_text(json.dumps(payload, indent=2), encoding="utf-8") + +if __name__ == "__main__": + main() diff --git a/src/didactopus/pack_validator.py b/src/didactopus/pack_validator.py new file mode 100644 index 0000000..763204f --- /dev/null +++ b/src/didactopus/pack_validator.py @@ -0,0 +1,141 @@ +from __future__ import annotations +from pathlib import Path +import yaml + +REQUIRED_FILES = ["pack.yaml", "concepts.yaml", "roadmap.yaml", "projects.yaml", "rubrics.yaml"] + +def _safe_load_yaml(path: Path, errors: list[str], label: str): + try: + return yaml.safe_load(path.read_text(encoding="utf-8")) or {} + except Exception as exc: + errors.append(f"Could not parse {label}: {exc}") + return {} + +def load_pack_artifacts(source_dir: str | Path) -> dict: + source = Path(source_dir) + errors: list[str] = [] + if not source.exists(): + return {"ok": False, "errors": [f"Source directory does not exist: {source}"], "warnings": [], "summary": {}, "artifacts": {}} + if not source.is_dir(): + return {"ok": False, "errors": [f"Source path is not a directory: {source}"], "warnings": [], "summary": {}, "artifacts": {}} + + for filename in REQUIRED_FILES: + if not (source / filename).exists(): + errors.append(f"Missing required file: {filename}") + if errors: + return {"ok": False, "errors": errors, "warnings": [], "summary": {}, "artifacts": {}} + + pack_data = _safe_load_yaml(source / "pack.yaml", errors, "pack.yaml") + concepts_data = _safe_load_yaml(source / "concepts.yaml", errors, "concepts.yaml") + roadmap_data = _safe_load_yaml(source / "roadmap.yaml", errors, "roadmap.yaml") + projects_data = _safe_load_yaml(source / "projects.yaml", errors, "projects.yaml") + rubrics_data = _safe_load_yaml(source / "rubrics.yaml", errors, "rubrics.yaml") + return { + "ok": len(errors) == 0, + "errors": errors, + "warnings": [], + "summary": {}, + "artifacts": { + "pack": pack_data, + "concepts": concepts_data, + "roadmap": roadmap_data, + "projects": projects_data, + "rubrics": rubrics_data, + }, + } + +def validate_pack_directory(source_dir: str | Path) -> dict: + loaded = load_pack_artifacts(source_dir) + errors = list(loaded["errors"]) + warnings = list(loaded["warnings"]) + summary = dict(loaded["summary"]) + if not loaded["ok"]: + return {"ok": False, "errors": errors, "warnings": warnings, "summary": summary} + + pack_data = loaded["artifacts"]["pack"] + concepts_data = loaded["artifacts"]["concepts"] + roadmap_data = loaded["artifacts"]["roadmap"] + projects_data = loaded["artifacts"]["projects"] + rubrics_data = loaded["artifacts"]["rubrics"] + + for field in ["name", "display_name", "version"]: + if field not in pack_data: + warnings.append(f"pack.yaml has no '{field}' field.") + + concepts = concepts_data.get("concepts", []) + roadmap_stages = roadmap_data.get("stages", []) + projects = projects_data.get("projects", []) + rubrics = rubrics_data.get("rubrics", []) + + if not isinstance(concepts, list): + errors.append("concepts.yaml top-level 'concepts' is not a list.") + concepts = [] + if not isinstance(roadmap_stages, list): + errors.append("roadmap.yaml top-level 'stages' is not a list.") + roadmap_stages = [] + if not isinstance(projects, list): + errors.append("projects.yaml top-level 'projects' is not a list.") + projects = [] + if not isinstance(rubrics, list): + errors.append("rubrics.yaml top-level 'rubrics' is not a list.") + rubrics = [] + + concept_ids = [] + for idx, concept in enumerate(concepts): + cid = concept.get("id", "") + if not cid: + errors.append(f"Concept at index {idx} has no id.") + else: + concept_ids.append(cid) + if not concept.get("title"): + warnings.append(f"Concept '{cid or idx}' has no title.") + desc = str(concept.get("description", "") or "") + if len(desc.strip()) < 12: + warnings.append(f"Concept '{cid or idx}' has a very thin description.") + + seen = set() + dups = set() + for cid in concept_ids: + if cid in seen: + dups.add(cid) + seen.add(cid) + for cid in sorted(dups): + errors.append(f"Duplicate concept id: {cid}") + + concept_id_set = set(concept_ids) + + for stage in roadmap_stages: + for cid in stage.get("concepts", []) or []: + if cid not in concept_id_set: + errors.append(f"roadmap.yaml references missing concept id: {cid}") + + for project in projects: + if not project.get("id"): + warnings.append("A project entry has no id.") + for cid in project.get("prerequisites", []) or []: + if cid not in concept_id_set: + errors.append(f"projects.yaml references missing prerequisite concept id: {cid}") + + for idx, rubric in enumerate(rubrics): + if not rubric.get("id"): + warnings.append(f"Rubric at index {idx} has no id.") + criteria = rubric.get("criteria", []) + if criteria is None: + warnings.append(f"Rubric '{rubric.get('id', idx)}' has null criteria.") + elif isinstance(criteria, list) and len(criteria) == 0: + warnings.append(f"Rubric '{rubric.get('id', idx)}' has empty criteria.") + elif not isinstance(criteria, list): + errors.append(f"Rubric '{rubric.get('id', idx)}' criteria is not a list.") + + summary = { + "pack_name": pack_data.get("name", ""), + "display_name": pack_data.get("display_name", ""), + "version": pack_data.get("version", ""), + "concept_count": len(concepts), + "roadmap_stage_count": len(roadmap_stages), + "project_count": len(projects), + "rubric_count": len(rubrics), + "error_count": len(errors), + "warning_count": len(warnings), + } + return {"ok": len(errors) == 0, "errors": errors, "warnings": warnings, "summary": summary} diff --git a/src/didactopus/path_quality_qa.py b/src/didactopus/path_quality_qa.py new file mode 100644 index 0000000..fd2c6ed --- /dev/null +++ b/src/didactopus/path_quality_qa.py @@ -0,0 +1,2 @@ +def path_quality_for_pack(source_dir): + return {'warnings': [], 'summary': {'path_warning_count': 0}} diff --git a/src/didactopus/progression_engine.py b/src/didactopus/progression_engine.py new file mode 100644 index 0000000..07326cf --- /dev/null +++ b/src/didactopus/progression_engine.py @@ -0,0 +1,31 @@ +from __future__ import annotations +from .learner_state import LearnerState, EvidenceEvent, MasteryRecord + +def apply_evidence( + state: LearnerState, + event: EvidenceEvent, + decay: float = 0.05, + reinforcement: float = 0.25, +) -> LearnerState: + rec = state.get_record(event.concept_id, event.dimension) + if rec is None: + rec = MasteryRecord( + concept_id=event.concept_id, + dimension=event.dimension, + score=0.0, + confidence=0.0, + evidence_count=0, + last_updated=event.timestamp, + ) + state.records.append(rec) + + weight = max(0.05, min(1.0, event.confidence_hint)) + rec.score = ((rec.score * rec.evidence_count) + (event.score * weight)) / max(1, rec.evidence_count + 1) + rec.confidence = min( + 1.0, + max(0.0, rec.confidence * (1.0 - decay) + reinforcement * weight + 0.10 * max(0.0, min(1.0, event.score))), + ) + rec.evidence_count += 1 + rec.last_updated = event.timestamp + state.history.append(event) + return state diff --git a/src/didactopus/provenance.py b/src/didactopus/provenance.py new file mode 100644 index 0000000..c87841d --- /dev/null +++ b/src/didactopus/provenance.py @@ -0,0 +1,20 @@ +from __future__ import annotations +from pathlib import Path +import json, yaml +from .source_models import SourceInventory + +def load_sources(path: str | Path) -> SourceInventory: + data = yaml.safe_load(Path(path).read_text(encoding="utf-8")) or {} + return SourceInventory.model_validate(data) + +def build_provenance_manifest(inventory: SourceInventory) -> dict: + return { + "source_count": len(inventory.sources), + "sources": [s.model_dump() for s in inventory.sources], + "licenses_present": sorted({s.license_id for s in inventory.sources if s.license_id}), + "excluded_source_count": sum(1 for s in inventory.sources if s.excluded_from_upstream_license), + "adapted_source_count": sum(1 for s in inventory.sources if s.adapted), + } + +def write_provenance_manifest(inventory: SourceInventory, outpath: str | Path) -> None: + Path(outpath).write_text(json.dumps(build_provenance_manifest(inventory), indent=2), encoding="utf-8") diff --git a/src/didactopus/readiness.py b/src/didactopus/readiness.py new file mode 100644 index 0000000..8e878a8 --- /dev/null +++ b/src/didactopus/readiness.py @@ -0,0 +1,22 @@ +from __future__ import annotations +from .learner_state import LearnerState + +def concept_dimension_score(state: LearnerState, concept_id: str, dimension: str) -> tuple[float, float]: + rec = state.get_record(concept_id, dimension) + if rec is None: + return 0.0, 0.0 + return rec.score, rec.confidence + +def concept_ready( + state: LearnerState, + concept_id: str, + prerequisite_ids: list[str], + dimension: str = "mastery", + min_score: float = 0.65, + min_confidence: float = 0.45, +) -> bool: + for prereq in prerequisite_ids: + score, conf = concept_dimension_score(state, prereq, dimension) + if score < min_score or conf < min_confidence: + return False + return True diff --git a/src/didactopus/recommendations.py b/src/didactopus/recommendations.py new file mode 100644 index 0000000..515980b --- /dev/null +++ b/src/didactopus/recommendations.py @@ -0,0 +1,40 @@ +from __future__ import annotations +from .learner_state import LearnerState +from .readiness import concept_ready + +def recommend_next_concepts( + state: LearnerState, + concepts: list[dict], + dimension: str = "mastery", + min_score: float = 0.65, + min_confidence: float = 0.45, +) -> list[dict]: + recs = [] + for concept in concepts: + cid = concept.get("id") + prereqs = list(concept.get("prerequisites", []) or []) + ready = concept_ready(state, cid, prereqs, dimension=dimension, min_score=min_score, min_confidence=min_confidence) + if ready: + existing = state.get_record(cid, dimension) + if existing is None or existing.score < min_score or existing.confidence < min_confidence: + recs.append({ + "concept_id": cid, + "title": concept.get("title", cid), + "reason": "prerequisites satisfied but mastery not yet secure", + }) + return recs + +def recommend_reinforcement_targets( + state: LearnerState, + dimension: str = "mastery", + low_confidence_threshold: float = 0.35, +) -> list[dict]: + out = [] + for rec in state.records: + if rec.dimension == dimension and rec.confidence < low_confidence_threshold: + out.append({ + "concept_id": rec.concept_id, + "dimension": rec.dimension, + "reason": "confidence low; reinforce with fresh evidence", + }) + return out diff --git a/src/didactopus/render_bundle.py b/src/didactopus/render_bundle.py new file mode 100644 index 0000000..7e29dff --- /dev/null +++ b/src/didactopus/render_bundle.py @@ -0,0 +1,25 @@ +from __future__ import annotations +import json +from pathlib import Path +from .export_svg import export_svg_frames + +def make_render_bundle(payload_json: str, out_dir: str, fps: int = 2, fmt: str = "gif"): + out = Path(out_dir) + frames_dir = out / "frames" + out.mkdir(parents=True, exist_ok=True) + export_svg_frames(payload_json, str(frames_dir)) + manifest = { + "input_payload": payload_json, + "frames_dir": str(frames_dir), + "fps": fps, + "format": fmt, + "expected_output": str(out / f"animation.{fmt}"), + "ffmpeg_example": f"ffmpeg -framerate {fps} -pattern_type glob -i '{frames_dir}/*.svg' {out / ('animation.' + fmt)}", + } + (out / "render_manifest.json").write_text(json.dumps(manifest, indent=2), encoding="utf-8") + script = "\n".join([ + "#!/usr/bin/env bash", + "set -euo pipefail", + f"ffmpeg -framerate {fps} -pattern_type glob -i '{frames_dir}/*.svg' '{out / ('animation.' + fmt)}'", + ]) + (out / "render.sh").write_text(script, encoding="utf-8") diff --git a/src/didactopus/repository.py b/src/didactopus/repository.py new file mode 100644 index 0000000..c1f454b --- /dev/null +++ b/src/didactopus/repository.py @@ -0,0 +1,283 @@ +from __future__ import annotations +import json +from datetime import datetime, timezone +from sqlalchemy import select +from .db import SessionLocal +from .orm import ( + UserORM, PackORM, LearnerORM, MasteryRecordORM, + KnowledgeCandidateORM, ReviewRecordORM, PromotionRecordORM, SynthesisCandidateORM +) +from .auth import verify_password + +def now_iso() -> str: + return datetime.now(timezone.utc).isoformat() + +def get_user_by_username(username: str): + with SessionLocal() as db: + return db.execute(select(UserORM).where(UserORM.username == username)).scalar_one_or_none() + +def get_user_by_id(user_id: int): + with SessionLocal() as db: + return db.get(UserORM, user_id) + +def authenticate_user(username: str, password: str): + user = get_user_by_username(username) + if user is None or not verify_password(password, user.password_hash) or not user.is_active: + return None + return user + +def list_packs(): + with SessionLocal() as db: + return db.execute(select(PackORM).order_by(PackORM.id)).scalars().all() + +def get_pack(pack_id: str): + with SessionLocal() as db: + return db.get(PackORM, pack_id) + +def create_learner(owner_user_id: int, learner_id: str, display_name: str = ""): + with SessionLocal() as db: + if db.get(LearnerORM, learner_id) is None: + db.add(LearnerORM(id=learner_id, owner_user_id=owner_user_id, display_name=display_name)) + db.commit() + +def learner_owned_by_user(user_id: int, learner_id: str) -> bool: + with SessionLocal() as db: + learner = db.get(LearnerORM, learner_id) + return learner is not None and learner.owner_user_id == user_id + +def list_mastery_records(learner_id: str): + with SessionLocal() as db: + rows = db.execute(select(MasteryRecordORM).where(MasteryRecordORM.learner_id == learner_id)).scalars().all() + return [{ + "concept_id": r.concept_id, + "dimension": r.dimension, + "score": r.score, + "confidence": r.confidence, + "evidence_count": r.evidence_count, + "last_updated": r.last_updated, + } for r in rows] + +def create_candidate(payload): + with SessionLocal() as db: + row = KnowledgeCandidateORM( + source_type=payload.source_type, + source_artifact_id=payload.source_artifact_id, + learner_id=payload.learner_id, + pack_id=payload.pack_id, + candidate_kind=payload.candidate_kind, + title=payload.title, + summary=payload.summary, + structured_payload_json=json.dumps(payload.structured_payload), + evidence_summary=payload.evidence_summary, + confidence_hint=payload.confidence_hint, + novelty_score=payload.novelty_score, + synthesis_score=payload.synthesis_score, + triage_lane=payload.triage_lane, + current_status="triaged", + created_at=now_iso(), + ) + db.add(row) + db.commit() + db.refresh(row) + return row.id + +def list_candidates(): + with SessionLocal() as db: + rows = db.execute(select(KnowledgeCandidateORM).order_by(KnowledgeCandidateORM.id.desc())).scalars().all() + return [{ + "candidate_id": r.id, + "source_type": r.source_type, + "source_artifact_id": r.source_artifact_id, + "learner_id": r.learner_id, + "pack_id": r.pack_id, + "candidate_kind": r.candidate_kind, + "title": r.title, + "summary": r.summary, + "structured_payload": json.loads(r.structured_payload_json or "{}"), + "evidence_summary": r.evidence_summary, + "confidence_hint": r.confidence_hint, + "novelty_score": r.novelty_score, + "synthesis_score": r.synthesis_score, + "triage_lane": r.triage_lane, + "current_status": r.current_status, + "created_at": r.created_at, + } for r in rows] + +def get_candidate(candidate_id: int): + with SessionLocal() as db: + r = db.get(KnowledgeCandidateORM, candidate_id) + if r is None: + return None + return { + "candidate_id": r.id, + "source_type": r.source_type, + "source_artifact_id": r.source_artifact_id, + "learner_id": r.learner_id, + "pack_id": r.pack_id, + "candidate_kind": r.candidate_kind, + "title": r.title, + "summary": r.summary, + "structured_payload": json.loads(r.structured_payload_json or "{}"), + "evidence_summary": r.evidence_summary, + "confidence_hint": r.confidence_hint, + "novelty_score": r.novelty_score, + "synthesis_score": r.synthesis_score, + "triage_lane": r.triage_lane, + "current_status": r.current_status, + "created_at": r.created_at, + } + +def update_candidate(candidate_id: int, triage_lane=None, current_status=None): + with SessionLocal() as db: + row = db.get(KnowledgeCandidateORM, candidate_id) + if row is None: + return None + if triage_lane is not None: + row.triage_lane = triage_lane + if current_status is not None: + row.current_status = current_status + db.commit() + db.refresh(row) + return row + +def create_review(candidate_id: int, reviewer_id: int, payload): + with SessionLocal() as db: + row = ReviewRecordORM( + candidate_id=candidate_id, + reviewer_id=reviewer_id, + review_kind=payload.review_kind, + verdict=payload.verdict, + rationale=payload.rationale, + requested_changes=payload.requested_changes, + created_at=now_iso(), + ) + db.add(row) + db.commit() + db.refresh(row) + return row.id + +def list_reviews(candidate_id: int): + with SessionLocal() as db: + rows = db.execute(select(ReviewRecordORM).where(ReviewRecordORM.candidate_id == candidate_id).order_by(ReviewRecordORM.id.desc())).scalars().all() + return [{ + "review_id": r.id, + "candidate_id": r.candidate_id, + "reviewer_id": r.reviewer_id, + "review_kind": r.review_kind, + "verdict": r.verdict, + "rationale": r.rationale, + "requested_changes": r.requested_changes, + "created_at": r.created_at, + } for r in rows] + +def create_promotion(candidate_id: int, promoted_by: int, payload): + with SessionLocal() as db: + row = PromotionRecordORM( + candidate_id=candidate_id, + promotion_target=payload.promotion_target, + target_object_id=payload.target_object_id, + promotion_status=payload.promotion_status, + promoted_by=promoted_by, + created_at=now_iso(), + ) + db.add(row) + candidate = db.get(KnowledgeCandidateORM, candidate_id) + if candidate: + candidate.current_status = "promoted" + candidate.triage_lane = payload.promotion_target + db.commit() + db.refresh(row) + return row.id + +def list_promotions(): + with SessionLocal() as db: + rows = db.execute(select(PromotionRecordORM).order_by(PromotionRecordORM.id.desc())).scalars().all() + return [{ + "promotion_id": r.id, + "candidate_id": r.candidate_id, + "promotion_target": r.promotion_target, + "target_object_id": r.target_object_id, + "promotion_status": r.promotion_status, + "promoted_by": r.promoted_by, + "created_at": r.created_at, + } for r in rows] + +def create_synthesis_candidate( + source_concept_id: str, + target_concept_id: str, + source_pack_id: str, + target_pack_id: str, + synthesis_kind: str, + score_semantic: float, + score_structural: float, + score_trajectory: float, + score_review_history: float, + explanation: str, + evidence: dict +): + score_total = 0.35 * score_semantic + 0.25 * score_structural + 0.20 * score_trajectory + 0.10 * score_review_history + 0.10 * evidence.get("novelty", 0.0) + with SessionLocal() as db: + row = SynthesisCandidateORM( + source_concept_id=source_concept_id, + target_concept_id=target_concept_id, + source_pack_id=source_pack_id, + target_pack_id=target_pack_id, + synthesis_kind=synthesis_kind, + score_total=score_total, + score_semantic=score_semantic, + score_structural=score_structural, + score_trajectory=score_trajectory, + score_review_history=score_review_history, + explanation=explanation, + evidence_json=json.dumps(evidence), + current_status="proposed", + created_at=now_iso(), + ) + db.add(row) + db.commit() + db.refresh(row) + return row.id + +def list_synthesis_candidates(): + with SessionLocal() as db: + rows = db.execute(select(SynthesisCandidateORM).order_by(SynthesisCandidateORM.score_total.desc(), SynthesisCandidateORM.id.desc())).scalars().all() + return [{ + "synthesis_id": r.id, + "source_concept_id": r.source_concept_id, + "target_concept_id": r.target_concept_id, + "source_pack_id": r.source_pack_id, + "target_pack_id": r.target_pack_id, + "synthesis_kind": r.synthesis_kind, + "score_total": r.score_total, + "score_semantic": r.score_semantic, + "score_structural": r.score_structural, + "score_trajectory": r.score_trajectory, + "score_review_history": r.score_review_history, + "explanation": r.explanation, + "evidence": json.loads(r.evidence_json or "{}"), + "current_status": r.current_status, + "created_at": r.created_at, + } for r in rows] + +def get_synthesis_candidate(synthesis_id: int): + with SessionLocal() as db: + r = db.get(SynthesisCandidateORM, synthesis_id) + if r is None: + return None + return { + "synthesis_id": r.id, + "source_concept_id": r.source_concept_id, + "target_concept_id": r.target_concept_id, + "source_pack_id": r.source_pack_id, + "target_pack_id": r.target_pack_id, + "synthesis_kind": r.synthesis_kind, + "score_total": r.score_total, + "score_semantic": r.score_semantic, + "score_structural": r.score_structural, + "score_trajectory": r.score_trajectory, + "score_review_history": r.score_review_history, + "explanation": r.explanation, + "evidence": json.loads(r.evidence_json or "{}"), + "current_status": r.current_status, + "created_at": r.created_at, + } diff --git a/src/didactopus/review_actions.py b/src/didactopus/review_actions.py index 1a1dd25..0910084 100644 --- a/src/didactopus/review_actions.py +++ b/src/didactopus/review_actions.py @@ -1,18 +1,14 @@ from __future__ import annotations - from .review_schema import ReviewAction, ReviewLedgerEntry, ReviewSession - def _find_concept(session: ReviewSession, concept_id: str): for concept in session.draft_pack.concepts: if concept.concept_id == concept_id: return concept return None - def apply_action(session: ReviewSession, reviewer: str, action: ReviewAction) -> None: target = _find_concept(session, action.target) - if action.action_type == "set_status" and target is not None: target.status = action.payload.get("status", target.status) elif action.action_type == "edit_prerequisites" and target is not None: @@ -21,13 +17,10 @@ def apply_action(session: ReviewSession, reviewer: str, action: ReviewAction) -> target.title = action.payload.get("title", target.title) elif action.action_type == "edit_description" and target is not None: target.description = action.payload.get("description", target.description) + elif action.action_type == "edit_notes" and target is not None: + target.notes = list(action.payload.get("notes", target.notes)) elif action.action_type == "resolve_conflict": - text = action.payload.get("conflict", "") - if text in session.draft_pack.conflicts: - session.draft_pack.conflicts.remove(text) - elif action.action_type == "note" and target is not None: - note = action.payload.get("note", "") - if note: - target.notes.append(note) - + conflict = action.payload.get("conflict", "") + if conflict in session.draft_pack.conflicts: + session.draft_pack.conflicts.remove(conflict) session.ledger.append(ReviewLedgerEntry(reviewer=reviewer, action=action)) diff --git a/src/didactopus/review_bridge.py b/src/didactopus/review_bridge.py new file mode 100644 index 0000000..5d165a9 --- /dev/null +++ b/src/didactopus/review_bridge.py @@ -0,0 +1,50 @@ +from __future__ import annotations +from pathlib import Path +import json +from .review_loader import load_draft_pack +from .review_schema import ReviewSession, ReviewAction +from .review_actions import apply_action +from .review_export import export_review_state_json, export_promoted_pack + +class ReviewWorkspaceBridge: + def __init__(self, workspace_dir: str | Path, reviewer: str = "Unknown Reviewer") -> None: + self.workspace_dir = Path(workspace_dir) + self.reviewer = reviewer + self.workspace_dir.mkdir(parents=True, exist_ok=True) + + @property + def draft_pack_dir(self) -> Path: + return self.workspace_dir / "draft_pack" + + @property + def review_session_path(self) -> Path: + return self.workspace_dir / "review_session.json" + + @property + def promoted_pack_dir(self) -> Path: + return self.workspace_dir / "promoted_pack" + + def load_session(self) -> ReviewSession: + if self.review_session_path.exists(): + data = json.loads(self.review_session_path.read_text(encoding="utf-8")) + return ReviewSession.model_validate(data) + draft = load_draft_pack(self.draft_pack_dir) + session = ReviewSession(reviewer=self.reviewer, draft_pack=draft) + export_review_state_json(session, self.review_session_path) + return session + + def save_session(self, session: ReviewSession) -> None: + export_review_state_json(session, self.review_session_path) + + def apply_actions(self, actions: list[dict]) -> ReviewSession: + session = self.load_session() + for action_dict in actions: + action = ReviewAction.model_validate(action_dict) + apply_action(session, session.reviewer, action) + self.save_session(session) + return session + + def export_promoted(self) -> ReviewSession: + session = self.load_session() + export_promoted_pack(session, self.promoted_pack_dir) + return session diff --git a/src/didactopus/review_bridge_server.py b/src/didactopus/review_bridge_server.py new file mode 100644 index 0000000..e206d64 --- /dev/null +++ b/src/didactopus/review_bridge_server.py @@ -0,0 +1,110 @@ +from __future__ import annotations +import argparse +from http.server import BaseHTTPRequestHandler, HTTPServer +import json +from pathlib import Path +from .config import load_config +from .review_bridge import ReviewWorkspaceBridge +from .workspace_manager import WorkspaceManager + +def json_response(handler: BaseHTTPRequestHandler, status: int, payload: dict) -> None: + body = json.dumps(payload, indent=2).encode("utf-8") + handler.send_response(status) + handler.send_header("Content-Type", "application/json") + handler.send_header("Content-Length", str(len(body))) + handler.send_header("Access-Control-Allow-Origin", "*") + handler.send_header("Access-Control-Allow-Methods", "GET,POST,OPTIONS") + handler.send_header("Access-Control-Allow-Headers", "Content-Type") + handler.end_headers() + handler.wfile.write(body) + +class ReviewBridgeHandler(BaseHTTPRequestHandler): + reviewer: str = "Unknown Reviewer" + workspace_manager: WorkspaceManager = None # type: ignore + active_bridge: ReviewWorkspaceBridge | None = None + active_workspace_id: str | None = None + + @classmethod + def set_active_workspace(cls, workspace_id: str) -> bool: + meta = cls.workspace_manager.touch_recent(workspace_id) + if meta is None: + return False + cls.active_workspace_id = workspace_id + cls.active_bridge = ReviewWorkspaceBridge(meta.path, reviewer=cls.reviewer) + return True + + def do_OPTIONS(self): + json_response(self, 200, {"ok": True}) + + def do_GET(self): + if self.path == "/api/workspaces": + reg = self.workspace_manager.list_workspaces() + json_response(self, 200, reg.model_dump()) + return + if self.path == "/api/load": + if self.active_bridge is None: + json_response(self, 400, {"error": "no active workspace"}) + return + session = self.active_bridge.load_session() + json_response(self, 200, {"workspace_id": self.active_workspace_id, "session": session.model_dump()}) + return + json_response(self, 404, {"error": "not found"}) + + def do_POST(self): + length = int(self.headers.get("Content-Length", "0")) + raw = self.rfile.read(length) if length else b"{}" + payload = json.loads(raw.decode("utf-8") or "{}") + + if self.path == "/api/workspaces/create": + meta = self.workspace_manager.create_workspace( + workspace_id=payload["workspace_id"], + title=payload["title"], + notes=payload.get("notes", "") + ) + self.set_active_workspace(meta.workspace_id) + json_response(self, 200, {"ok": True, "workspace": meta.model_dump()}) + return + + if self.path == "/api/workspaces/open": + ok = self.set_active_workspace(payload["workspace_id"]) + if not ok: + json_response(self, 404, {"error": "workspace not found"}) + return + json_response(self, 200, {"ok": True, "workspace_id": self.active_workspace_id}) + return + + if self.active_bridge is None: + json_response(self, 400, {"error": "no active workspace"}) + return + + if self.path == "/api/save": + session = self.active_bridge.apply_actions(payload.get("actions", [])) + json_response(self, 200, {"ok": True, "workspace_id": self.active_workspace_id, "session": session.model_dump()}) + return + + if self.path == "/api/export": + session = self.active_bridge.export_promoted() + json_response(self, 200, {"ok": True, "promoted_pack_dir": str(self.active_bridge.promoted_pack_dir), "workspace_id": self.active_workspace_id, "session": session.model_dump()}) + return + + json_response(self, 404, {"error": "not found"}) + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Didactopus local review bridge server with workspace manager") + parser.add_argument("--config", default="configs/config.example.yaml") + return parser + +def main() -> None: + args = build_parser().parse_args() + config = load_config(Path(args.config)) + ReviewBridgeHandler.reviewer = config.review.default_reviewer + ReviewBridgeHandler.workspace_manager = WorkspaceManager( + registry_path=config.bridge.registry_path, + default_workspace_root=config.bridge.default_workspace_root + ) + server = HTTPServer((config.bridge.host, config.bridge.port), ReviewBridgeHandler) + print(f"Didactopus review bridge listening on http://{config.bridge.host}:{config.bridge.port}") + server.serve_forever() + +if __name__ == "__main__": + main() diff --git a/src/didactopus/review_export.py b/src/didactopus/review_export.py index 873ca97..9ffafdc 100644 --- a/src/didactopus/review_export.py +++ b/src/didactopus/review_export.py @@ -1,32 +1,23 @@ from __future__ import annotations - from pathlib import Path -import json -import yaml - +import json, yaml from .review_schema import ReviewSession - def export_review_state_json(session: ReviewSession, path: str | Path) -> None: Path(path).write_text(session.model_dump_json(indent=2), encoding="utf-8") - def export_promoted_pack(session: ReviewSession, outdir: str | Path) -> None: outdir = Path(outdir) outdir.mkdir(parents=True, exist_ok=True) - promoted_pack = dict(session.draft_pack.pack) promoted_pack["version"] = str(promoted_pack.get("version", "0.1.0-draft")).replace("-draft", "-reviewed") - promoted_pack["curation"] = { - "reviewer": session.reviewer, - "ledger_entries": len(session.ledger), - } + promoted_pack["curation"] = {"reviewer": session.reviewer, "ledger_entries": len(session.ledger)} - trusted_concepts = [] + concepts = [] for concept in session.draft_pack.concepts: if concept.status == "rejected": continue - trusted_concepts.append({ + concepts.append({ "id": concept.concept_id, "title": concept.title, "description": concept.description, @@ -38,20 +29,6 @@ def export_promoted_pack(session: ReviewSession, outdir: str | Path) -> None: }) (outdir / "pack.yaml").write_text(yaml.safe_dump(promoted_pack, sort_keys=False), encoding="utf-8") - (outdir / "concepts.yaml").write_text(yaml.safe_dump({"concepts": trusted_concepts}, sort_keys=False), encoding="utf-8") + (outdir / "concepts.yaml").write_text(yaml.safe_dump({"concepts": concepts}, sort_keys=False), encoding="utf-8") (outdir / "review_ledger.json").write_text(json.dumps(session.model_dump(), indent=2), encoding="utf-8") (outdir / "license_attribution.json").write_text(json.dumps(session.draft_pack.attribution, indent=2), encoding="utf-8") - - -def export_review_ui_data(session: ReviewSession, outdir: str | Path) -> None: - outdir = Path(outdir) - outdir.mkdir(parents=True, exist_ok=True) - data = { - "reviewer": session.reviewer, - "pack": session.draft_pack.pack, - "concepts": [c.model_dump() for c in session.draft_pack.concepts], - "conflicts": session.draft_pack.conflicts, - "review_flags": session.draft_pack.review_flags, - "ledger": [entry.model_dump() for entry in session.ledger], - } - (outdir / "review_data.json").write_text(json.dumps(data, indent=2), encoding="utf-8") diff --git a/src/didactopus/review_loader.py b/src/didactopus/review_loader.py index 8498a12..e21269f 100644 --- a/src/didactopus/review_loader.py +++ b/src/didactopus/review_loader.py @@ -1,12 +1,8 @@ from __future__ import annotations - from pathlib import Path -import json -import yaml - +import json, yaml from .review_schema import DraftPackData, ConceptReviewEntry - def load_draft_pack(pack_dir: str | Path) -> DraftPackData: pack_dir = Path(pack_dir) concepts_yaml = yaml.safe_load((pack_dir / "concepts.yaml").read_text(encoding="utf-8")) or {} @@ -19,31 +15,28 @@ def load_draft_pack(pack_dir: str | Path) -> DraftPackData: description=item.get("description", ""), prerequisites=list(item.get("prerequisites", [])), mastery_signals=list(item.get("mastery_signals", [])), + status=item.get("status", "needs_review"), + notes=list(item.get("notes", [])), ) ) - def bullet_lines(path: Path) -> list[str]: + def bullets(path: Path) -> list[str]: if not path.exists(): return [] return [line[2:] for line in path.read_text(encoding="utf-8").splitlines() if line.startswith("- ")] - conflicts = bullet_lines(pack_dir / "conflict_report.md") - review_flags = bullet_lines(pack_dir / "review_report.md") + pack = {} + if (pack_dir / "pack.yaml").exists(): + pack = yaml.safe_load((pack_dir / "pack.yaml").read_text(encoding="utf-8")) or {} attribution = {} - attribution_path = pack_dir / "license_attribution.json" - if attribution_path.exists(): - attribution = json.loads(attribution_path.read_text(encoding="utf-8")) - - pack = {} - pack_path = pack_dir / "pack.yaml" - if pack_path.exists(): - pack = yaml.safe_load(pack_path.read_text(encoding="utf-8")) or {} + if (pack_dir / "license_attribution.json").exists(): + attribution = json.loads((pack_dir / "license_attribution.json").read_text(encoding="utf-8")) return DraftPackData( pack=pack, concepts=concepts, - conflicts=conflicts, - review_flags=review_flags, + conflicts=bullets(pack_dir / "conflict_report.md"), + review_flags=bullets(pack_dir / "review_report.md"), attribution=attribution, ) diff --git a/src/didactopus/review_schema.py b/src/didactopus/review_schema.py index ff82ccd..5bc0db0 100644 --- a/src/didactopus/review_schema.py +++ b/src/didactopus/review_schema.py @@ -1,20 +1,8 @@ from __future__ import annotations - from pydantic import BaseModel, Field from typing import Literal TrustStatus = Literal["trusted", "provisional", "rejected", "needs_review"] -ActionType = Literal[ - "set_status", - "edit_prerequisites", - "edit_title", - "edit_description", - "merge_concepts", - "split_concept", - "resolve_conflict", - "note", -] - class ConceptReviewEntry(BaseModel): concept_id: str @@ -25,19 +13,6 @@ class ConceptReviewEntry(BaseModel): status: TrustStatus = "needs_review" notes: list[str] = Field(default_factory=list) - -class ReviewAction(BaseModel): - action_type: ActionType - target: str - payload: dict = Field(default_factory=dict) - rationale: str = "" - - -class ReviewLedgerEntry(BaseModel): - reviewer: str - action: ReviewAction - - class DraftPackData(BaseModel): pack: dict = Field(default_factory=dict) concepts: list[ConceptReviewEntry] = Field(default_factory=list) @@ -45,8 +20,29 @@ class DraftPackData(BaseModel): review_flags: list[str] = Field(default_factory=list) attribution: dict = Field(default_factory=dict) +class ReviewAction(BaseModel): + action_type: str + target: str = "" + payload: dict = Field(default_factory=dict) + rationale: str = "" + +class ReviewLedgerEntry(BaseModel): + reviewer: str + action: ReviewAction class ReviewSession(BaseModel): reviewer: str draft_pack: DraftPackData ledger: list[ReviewLedgerEntry] = Field(default_factory=list) + +class WorkspaceMeta(BaseModel): + workspace_id: str + title: str + path: str + created_at: str + last_opened_at: str + notes: str = "" + +class WorkspaceRegistry(BaseModel): + workspaces: list[WorkspaceMeta] = Field(default_factory=list) + recent_workspace_ids: list[str] = Field(default_factory=list) diff --git a/src/didactopus/sample_pack_loader.py b/src/didactopus/sample_pack_loader.py new file mode 100644 index 0000000..d43e1d2 --- /dev/null +++ b/src/didactopus/sample_pack_loader.py @@ -0,0 +1,7 @@ +from __future__ import annotations +from pathlib import Path +import yaml + +def load_concepts(path: str | Path) -> list[dict]: + data = yaml.safe_load(Path(path).read_text(encoding="utf-8")) or {} + return list(data.get("concepts", []) or []) diff --git a/src/didactopus/seed.py b/src/didactopus/seed.py new file mode 100644 index 0000000..7541e63 --- /dev/null +++ b/src/didactopus/seed.py @@ -0,0 +1,53 @@ +from __future__ import annotations +import json +from sqlalchemy import select +from .db import Base, engine, SessionLocal +from .orm import UserORM, PackORM +from .auth import hash_password + +def main(): + Base.metadata.create_all(bind=engine) + with SessionLocal() as db: + if db.execute(select(UserORM).where(UserORM.username == "wesley")).scalar_one_or_none() is None: + db.add(UserORM(username="wesley", password_hash=hash_password("demo-pass"), role="admin", is_active=True)) + if db.execute(select(UserORM).where(UserORM.username == "reviewer")).scalar_one_or_none() is None: + db.add(UserORM(username="reviewer", password_hash=hash_password("demo-pass"), role="reviewer", is_active=True)) + if db.get(PackORM, "biology-pack") is None: + db.add(PackORM( + id="biology-pack", + owner_user_id=1, + policy_lane="personal", + title="Biology Pack", + subtitle="Core biology concepts", + level="novice-friendly", + is_published=True, + data_json=json.dumps({ + "id": "biology-pack", + "title": "Biology Pack", + "concepts": [ + {"id": "selection", "title": "Natural Selection", "prerequisites": ["variation"]}, + {"id": "variation", "title": "Variation", "prerequisites": []}, + {"id": "drift", "title": "Genetic Drift", "prerequisites": ["variation"]} + ] + }) + )) + if db.get(PackORM, "math-pack") is None: + db.add(PackORM( + id="math-pack", + owner_user_id=1, + policy_lane="personal", + title="Math Pack", + subtitle="Core math concepts", + level="novice-friendly", + is_published=True, + data_json=json.dumps({ + "id": "math-pack", + "title": "Math Pack", + "concepts": [ + {"id": "random_walk", "title": "Random Walk", "prerequisites": ["variation"]}, + {"id": "variation", "title": "Variation in Models", "prerequisites": []}, + {"id": "optimization", "title": "Optimization", "prerequisites": []} + ] + }) + )) + db.commit() diff --git a/src/didactopus/semantic_qa.py b/src/didactopus/semantic_qa.py new file mode 100644 index 0000000..b6be58d --- /dev/null +++ b/src/didactopus/semantic_qa.py @@ -0,0 +1,91 @@ +from __future__ import annotations +from pathlib import Path +import re +from difflib import SequenceMatcher +from .pack_validator import load_pack_artifacts + +BROAD_HINTS = {"and", "overview", "foundations", "introduction", "basics", "advanced"} + +def normalize_title(text: str) -> str: + return re.sub(r"[^a-z0-9]+", " ", text.lower()).strip() + +def similarity(a: str, b: str) -> float: + return SequenceMatcher(None, normalize_title(a), normalize_title(b)).ratio() + +def token_set(text: str) -> set[str]: + return {t for t in normalize_title(text).split() if t} + +def semantic_qa_for_pack(source_dir: str | Path) -> dict: + loaded = load_pack_artifacts(source_dir) + if not loaded["ok"]: + return {"warnings": [], "summary": {"semantic_warning_count": 0}} + + pack = loaded["artifacts"]["pack"] + concepts = loaded["artifacts"]["concepts"].get("concepts", []) or [] + roadmap = loaded["artifacts"]["roadmap"].get("stages", []) or [] + + warnings: list[str] = [] + + # Near-duplicate titles + for i in range(len(concepts)): + for j in range(i + 1, len(concepts)): + a = concepts[i] + b = concepts[j] + sim = similarity(a.get("title", ""), b.get("title", "")) + if sim >= 0.86 and a.get("id") != b.get("id"): + warnings.append(f"Near-duplicate concept titles: '{a.get('title')}' vs '{b.get('title')}'") + + # Over-broad titles + for concept in concepts: + title = concept.get("title", "") + toks = token_set(title) + if len(toks) >= 3 and (BROAD_HINTS & toks): + warnings.append(f"Concept '{title}' may be over-broad and may need splitting.") + if " and " in title.lower(): + warnings.append(f"Concept '{title}' is compound and may combine multiple ideas.") + + # Similar descriptions + for i in range(len(concepts)): + for j in range(i + 1, len(concepts)): + da = str(concepts[i].get("description", "") or "") + db = str(concepts[j].get("description", "") or "") + if len(da) > 20 and len(db) > 20: + sim = SequenceMatcher(None, da.lower(), db.lower()).ratio() + if sim >= 0.82: + warnings.append( + f"Concept descriptions are very similar: '{concepts[i].get('title')}' vs '{concepts[j].get('title')}'" + ) + + # Thin prerequisite chains on advanced-sounding concepts + for concept in concepts: + title = normalize_title(concept.get("title", "")) + prereqs = concept.get("prerequisites", []) or [] + if any(h in title for h in ["advanced", "posterior", "model", "inference", "analysis"]) and len(prereqs) == 0: + warnings.append(f"Concept '{concept.get('title')}' looks advanced but has no prerequisites.") + + # Missing bridge concepts between roadmap stages + concept_by_id = {c.get("id"): c for c in concepts if c.get("id")} + for idx in range(len(roadmap) - 1): + current_stage = roadmap[idx] + next_stage = roadmap[idx + 1] + current_titles = [concept_by_id[cid].get("title", "") for cid in current_stage.get("concepts", []) if cid in concept_by_id] + next_titles = [concept_by_id[cid].get("title", "") for cid in next_stage.get("concepts", []) if cid in concept_by_id] + current_tokens = set().union(*[token_set(t) for t in current_titles]) if current_titles else set() + next_tokens = set().union(*[token_set(t) for t in next_titles]) if next_titles else set() + overlap = current_tokens & next_tokens + if current_titles and next_titles and len(overlap) == 0: + warnings.append( + f"Roadmap transition from stage '{current_stage.get('title')}' to '{next_stage.get('title')}' may lack a bridge concept." + ) + if len(next_titles) == 1 and len(current_titles) >= 2 and len(overlap) == 0: + warnings.append( + f"Stage '{next_stage.get('title')}' contains a singleton concept with weak visible continuity from the prior stage." + ) + + return { + "warnings": warnings, + "summary": { + "semantic_warning_count": len(warnings), + "pack_name": pack.get("name", ""), + }, + } diff --git a/src/didactopus/source_models.py b/src/didactopus/source_models.py new file mode 100644 index 0000000..7d3cd4e --- /dev/null +++ b/src/didactopus/source_models.py @@ -0,0 +1,21 @@ +from __future__ import annotations +from pydantic import BaseModel, Field + +class SourceRecord(BaseModel): + source_id: str + title: str + url: str + publisher: str = "" + creator: str = "" + license_id: str = "" + license_url: str = "" + retrieved_at: str = "" + adapted: bool = False + adaptation_notes: str = "" + attribution_text: str = "" + excluded_from_upstream_license: bool = False + exclusion_notes: str = "" + tags: list[str] = Field(default_factory=list) + +class SourceInventory(BaseModel): + sources: list[SourceRecord] = Field(default_factory=list) diff --git a/src/didactopus/stop_criteria.py b/src/didactopus/stop_criteria.py new file mode 100644 index 0000000..59cbb16 --- /dev/null +++ b/src/didactopus/stop_criteria.py @@ -0,0 +1,29 @@ +from __future__ import annotations +from .learner_state import LearnerState +from .orchestration_models import StopCriteria, RunState + +def assess_claim_readiness( + learner_state: LearnerState, + run_state: RunState, + criteria: StopCriteria, + dimension: str = "mastery", +) -> dict: + relevant = [r for r in learner_state.records if r.dimension == dimension] + mastered = [r for r in relevant if r.score >= criteria.min_average_score and r.confidence >= criteria.min_average_confidence] + avg_score = sum(r.score for r in relevant) / len(relevant) if relevant else 0.0 + avg_conf = sum(r.confidence for r in relevant) / len(relevant) if relevant else 0.0 + capstones_ok = all(cap in run_state.completed_capstones for cap in criteria.required_capstones) + + ready = ( + len(mastered) >= criteria.min_mastered_concepts + and avg_score >= criteria.min_average_score + and avg_conf >= criteria.min_average_confidence + and capstones_ok + ) + return { + "ready": ready, + "mastered_concepts": len(mastered), + "average_score": avg_score, + "average_confidence": avg_conf, + "capstones_ok": capstones_ok, + } diff --git a/src/didactopus/storage.py b/src/didactopus/storage.py new file mode 100644 index 0000000..ff8d759 --- /dev/null +++ b/src/didactopus/storage.py @@ -0,0 +1,35 @@ +from __future__ import annotations +from pathlib import Path +import json +from .models import PackData, LearnerState + +class FileStorage: + def __init__(self, base_dir: str | Path): + self.base_dir = Path(base_dir) + self.packs_dir = self.base_dir / "packs" + self.learners_dir = self.base_dir / "learners" + self.packs_dir.mkdir(parents=True, exist_ok=True) + self.learners_dir.mkdir(parents=True, exist_ok=True) + + def list_packs(self) -> list[PackData]: + out = [] + for path in sorted(self.packs_dir.glob("*.json")): + out.append(PackData.model_validate_json(path.read_text(encoding="utf-8"))) + return out + + def get_pack(self, pack_id: str) -> PackData | None: + path = self.packs_dir / f"{pack_id}.json" + if not path.exists(): + return None + return PackData.model_validate_json(path.read_text(encoding="utf-8")) + + def get_learner_state(self, learner_id: str) -> LearnerState: + path = self.learners_dir / f"{learner_id}.json" + if not path.exists(): + return LearnerState(learner_id=learner_id) + return LearnerState.model_validate_json(path.read_text(encoding="utf-8")) + + def save_learner_state(self, state: LearnerState) -> LearnerState: + path = self.learners_dir / f"{state.learner_id}.json" + path.write_text(state.model_dump_json(indent=2), encoding="utf-8") + return state diff --git a/src/didactopus/synthesis.py b/src/didactopus/synthesis.py new file mode 100644 index 0000000..a980f75 --- /dev/null +++ b/src/didactopus/synthesis.py @@ -0,0 +1,70 @@ +from __future__ import annotations +import json +from .repository import list_packs, create_synthesis_candidate + +def _pack_data(pack_row): + return json.loads(pack_row.data_json or "{}") + +def _concepts(pack_row): + return _pack_data(pack_row).get("concepts", []) + +def _norm(text: str) -> set[str]: + return {t.strip().lower() for t in text.replace("-", " ").replace("_", " ").split() if t.strip()} + +def _semantic_similarity(a: dict, b: dict) -> float: + sa = _norm(a.get("title", "")) | _norm(" ".join(a.get("prerequisites", []))) + sb = _norm(b.get("title", "")) | _norm(" ".join(b.get("prerequisites", []))) + if not sa or not sb: + return 0.0 + return len(sa & sb) / len(sa | sb) + +def _structural_similarity(a: dict, b: dict) -> float: + pa = set(a.get("prerequisites", [])) + pb = set(b.get("prerequisites", [])) + if not pa and not pb: + return 0.6 + if not pa or not pb: + return 0.2 + return len(pa & pb) / len(pa | pb) + +def generate_synthesis_candidates(source_pack_id: str | None = None, target_pack_id: str | None = None, limit: int = 20): + packs = list_packs() + by_id = {p.id: p for p in packs} + source_packs = [by_id[source_pack_id]] if source_pack_id and source_pack_id in by_id else packs + target_packs = [by_id[target_pack_id]] if target_pack_id and target_pack_id in by_id else packs + + created = [] + seen = set() + for sp in source_packs: + for tp in target_packs: + if sp.id == tp.id: + continue + for ca in _concepts(sp): + for cb in _concepts(tp): + sem = _semantic_similarity(ca, cb) + struct = _structural_similarity(ca, cb) + traj = 0.4 + review_prior = 0.5 + novelty = 1.0 if (ca.get("id"), cb.get("id")) not in seen else 0.0 + total = 0.35 * sem + 0.25 * struct + 0.20 * traj + 0.10 * review_prior + 0.10 * novelty + if total < 0.45: + continue + explanation = f"Possible cross-pack overlap between '{ca.get('title')}' and '{cb.get('title')}'." + sid = create_synthesis_candidate( + source_concept_id=ca.get("id", ""), + target_concept_id=cb.get("id", ""), + source_pack_id=sp.id, + target_pack_id=tp.id, + synthesis_kind="cross_pack_similarity", + score_semantic=sem, + score_structural=struct, + score_trajectory=traj, + score_review_history=review_prior, + explanation=explanation, + evidence={"novelty": novelty, "source_title": ca.get("title"), "target_title": cb.get("title")}, + ) + seen.add((ca.get("id"), cb.get("id"))) + created.append(sid) + if len(created) >= limit: + return created + return created diff --git a/src/didactopus/ux_feedback.py b/src/didactopus/ux_feedback.py new file mode 100644 index 0000000..7ef892a --- /dev/null +++ b/src/didactopus/ux_feedback.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +def format_recommendation_card(item: dict) -> dict: + title = item.get("title", item.get("concept_id", "Next concept")) + reason = item.get("reason", "Good next step.") + return { + "title": title, + "subtitle": "Recommended next step", + "body": reason, + "cta": f"Work on {title}", + } + +def format_reward_message(kind: str, label: str) -> str: + if kind == "unlock": + return f"Unlocked: {label}" + if kind == "milestone": + return f"Milestone reached: {label}" + if kind == "capstone": + return f"Capstone-ready: {label}" + return f"Progress recorded: {label}" diff --git a/src/didactopus/worker.py b/src/didactopus/worker.py new file mode 100644 index 0000000..6202bb3 --- /dev/null +++ b/src/didactopus/worker.py @@ -0,0 +1,43 @@ +from __future__ import annotations +import json, tempfile +from pathlib import Path +from datetime import datetime, timedelta, timezone +from .repository import update_render_job, register_artifact +from .render_bundle import make_render_bundle + +def future_iso(days: int) -> str: + return (datetime.now(timezone.utc) + timedelta(days=days)).isoformat() + +def process_render_job(job_id: int, learner_id: str, pack_id: str, fmt: str, fps: int, theme: str, retention_class: str, retention_days: int, animation_payload: dict): + update_render_job(job_id, status="running") + try: + base = Path(tempfile.mkdtemp(prefix=f"didactopus_job_{job_id}_")) + payload_json = base / "animation_payload.json" + payload_json.write_text(json.dumps(animation_payload, indent=2), encoding="utf-8") + out_dir = base / "bundle" + make_render_bundle(str(payload_json), str(out_dir), fps=fps, fmt=fmt) + manifest_path = out_dir / "render_manifest.json" + script_path = out_dir / "render.sh" + update_render_job( + job_id, + status="completed", + bundle_dir=str(out_dir), + payload_json=str(payload_json), + manifest_path=str(manifest_path), + script_path=str(script_path), + error_text="", + ) + register_artifact( + render_job_id=job_id, + learner_id=learner_id, + pack_id=pack_id, + artifact_type="render_bundle", + fmt=fmt, + title=f"{pack_id} animation bundle", + path=str(out_dir), + metadata={"fps": fps, "theme": theme, "manifest_path": str(manifest_path), "script_path": str(script_path)}, + retention_class=retention_class, + expires_at=future_iso(retention_days), + ) + except Exception as e: + update_render_job(job_id, status="failed", error_text=str(e)) diff --git a/src/didactopus/workspace_manager.py b/src/didactopus/workspace_manager.py new file mode 100644 index 0000000..36769f8 --- /dev/null +++ b/src/didactopus/workspace_manager.py @@ -0,0 +1,72 @@ +from __future__ import annotations +from pathlib import Path +from datetime import datetime, UTC +import json +from .review_schema import WorkspaceMeta, WorkspaceRegistry + +def utc_now() -> str: + return datetime.now(UTC).isoformat() + +class WorkspaceManager: + def __init__(self, registry_path: str | Path, default_workspace_root: str | Path) -> None: + self.registry_path = Path(registry_path) + self.default_workspace_root = Path(default_workspace_root) + self.default_workspace_root.mkdir(parents=True, exist_ok=True) + + def load_registry(self) -> WorkspaceRegistry: + if self.registry_path.exists(): + return WorkspaceRegistry.model_validate(json.loads(self.registry_path.read_text(encoding="utf-8"))) + return WorkspaceRegistry() + + def save_registry(self, registry: WorkspaceRegistry) -> None: + self.registry_path.write_text(registry.model_dump_json(indent=2), encoding="utf-8") + + def list_workspaces(self) -> WorkspaceRegistry: + return self.load_registry() + + def create_workspace(self, workspace_id: str, title: str, notes: str = "") -> WorkspaceMeta: + registry = self.load_registry() + workspace_dir = self.default_workspace_root / workspace_id + workspace_dir.mkdir(parents=True, exist_ok=True) + draft_dir = workspace_dir / "draft_pack" + draft_dir.mkdir(parents=True, exist_ok=True) + if not (draft_dir / "pack.yaml").exists(): + (draft_dir / "pack.yaml").write_text( + f"name: {workspace_id}\ndisplay_name: {title}\nversion: 0.1.0-draft\ndescription: Seed draft pack for workspace {workspace_id}\n", + encoding="utf-8" + ) + if not (draft_dir / "concepts.yaml").exists(): + (draft_dir / "concepts.yaml").write_text("concepts: []\n", encoding="utf-8") + + meta = WorkspaceMeta( + workspace_id=workspace_id, + title=title, + path=str(workspace_dir), + created_at=utc_now(), + last_opened_at=utc_now(), + notes=notes, + ) + registry.workspaces = [w for w in registry.workspaces if w.workspace_id != workspace_id] + [meta] + registry.recent_workspace_ids = [workspace_id] + [w for w in registry.recent_workspace_ids if w != workspace_id] + self.save_registry(registry) + return meta + + def touch_recent(self, workspace_id: str) -> WorkspaceMeta | None: + registry = self.load_registry() + target = None + for ws in registry.workspaces: + if ws.workspace_id == workspace_id: + ws.last_opened_at = utc_now() + target = ws + break + if target is not None: + registry.recent_workspace_ids = [workspace_id] + [w for w in registry.recent_workspace_ids if w != workspace_id] + self.save_registry(registry) + return target + + def get_workspace(self, workspace_id: str) -> WorkspaceMeta | None: + registry = self.load_registry() + for ws in registry.workspaces: + if ws.workspace_id == workspace_id: + return ws + return None diff --git a/tests/test_api_scaffold.py b/tests/test_api_scaffold.py new file mode 100644 index 0000000..d4c4848 --- /dev/null +++ b/tests/test_api_scaffold.py @@ -0,0 +1,6 @@ +from pathlib import Path + +def test_api_files_exist(): + assert Path("src/didactopus/api.py").exists() + assert Path("src/didactopus/storage.py").exists() + assert Path("data/packs/bayes-pack.json").exists() diff --git a/tests/test_attribution_builder.py b/tests/test_attribution_builder.py new file mode 100644 index 0000000..3bd8748 --- /dev/null +++ b/tests/test_attribution_builder.py @@ -0,0 +1,22 @@ +from pathlib import Path +from didactopus.attribution_builder import build_artifacts + +def test_build_artifacts(tmp_path: Path) -> None: + sources = tmp_path / "sources.yaml" + sources.write_text( + "sources:\n" + " - source_id: s1\n" + " title: Example\n" + " url: https://example.org\n" + " publisher: Example Publisher\n" + " creator: Example Creator\n" + " license_id: CC BY-NC-SA 4.0\n" + " license_url: https://creativecommons.org/licenses/by-nc-sa/4.0/\n" + " attribution_text: Example attribution\n", + encoding="utf-8", + ) + attr = tmp_path / "ATTRIBUTION.md" + manifest = tmp_path / "provenance_manifest.json" + build_artifacts(sources, attr, manifest) + assert attr.exists() + assert manifest.exists() diff --git a/tests/test_attribution_qa.py b/tests/test_attribution_qa.py new file mode 100644 index 0000000..f83dd45 --- /dev/null +++ b/tests/test_attribution_qa.py @@ -0,0 +1,17 @@ +from pathlib import Path +from didactopus.attribution_qa import attribution_qa + +def test_attribution_qa_detects_missing_fields(tmp_path: Path) -> None: + p = tmp_path / "sources.yaml" + p.write_text( + "sources:\n" + " - source_id: s1\n" + " title: Example\n" + " url: ''\n" + " license_id: CC BY-NC-SA 4.0\n" + " license_url: ''\n" + " attribution_text: ''\n", + encoding="utf-8", + ) + result = attribution_qa(p) + assert len(result["warnings"]) >= 3 diff --git a/tests/test_backend_files.py b/tests/test_backend_files.py new file mode 100644 index 0000000..8d551d9 --- /dev/null +++ b/tests/test_backend_files.py @@ -0,0 +1,6 @@ +from pathlib import Path + +def test_backend_scaffold_exists(): + assert Path("src/didactopus/api.py").exists() + assert Path("src/didactopus/orm.py").exists() + assert Path("src/didactopus/seed.py").exists() diff --git a/tests/test_compliance.py b/tests/test_compliance.py new file mode 100644 index 0000000..ad0c306 --- /dev/null +++ b/tests/test_compliance.py @@ -0,0 +1,21 @@ +from pathlib import Path +from didactopus.course_ingestion_compliance import load_sources, build_pack_compliance_manifest, compliance_qa + +def test_build_manifest_and_qa(tmp_path: Path): + p = tmp_path / "sources.yaml" + p.write_text( + "sources:\n" + " - source_id: s1\n" + " title: Example\n" + " url: https://example.org\n" + " license_id: CC BY-NC-SA 4.0\n" + " license_url: https://creativecommons.org/licenses/by-nc-sa/4.0/\n" + " attribution_text: Example attribution\n", + encoding="utf-8", + ) + inv = load_sources(p) + manifest = build_pack_compliance_manifest("p1", "Pack 1", inv) + qa = compliance_qa(inv, manifest) + assert manifest.share_alike_required is True + assert manifest.noncommercial_only is True + assert qa["summary"]["source_count"] == 1 diff --git a/tests/test_coverage_alignment_qa.py b/tests/test_coverage_alignment_qa.py new file mode 100644 index 0000000..1e0e4c7 --- /dev/null +++ b/tests/test_coverage_alignment_qa.py @@ -0,0 +1,20 @@ +from pathlib import Path +from didactopus.coverage_alignment_qa import coverage_alignment_for_pack + +def make_pack(base: Path, concepts_yaml: str, roadmap_yaml: str, projects_yaml: str, rubrics_yaml: str): + (base / "pack.yaml").write_text("name: p\ndisplay_name: P\nversion: 0.1.0\n", encoding="utf-8") + (base / "concepts.yaml").write_text(concepts_yaml, encoding="utf-8") + (base / "roadmap.yaml").write_text(roadmap_yaml, encoding="utf-8") + (base / "projects.yaml").write_text(projects_yaml, encoding="utf-8") + (base / "rubrics.yaml").write_text(rubrics_yaml, encoding="utf-8") + +def test_detects_uncovered_concepts(tmp_path: Path) -> None: + make_pack( + tmp_path, + "concepts:\n - id: c1\n title: Foundations\n description: enough description here\n mastery_signals: [Explain foundations]\n - id: c2\n title: Detached Topic\n description: enough description here\n mastery_signals: [Explain detached topic]\n", + "stages:\n - id: s1\n title: One\n concepts: [c1]\n checkpoint: [Explain foundations]\n", + "projects:\n - id: p1\n title: Project\n prerequisites: [c1]\n deliverables: [short report]\n", + "rubrics:\n - id: r1\n title: Basic\n criteria: [correctness]\n", + ) + result = coverage_alignment_for_pack(tmp_path) + assert any("c2" in w for w in result["warnings"]) diff --git a/tests/test_evaluator_alignment_qa.py b/tests/test_evaluator_alignment_qa.py new file mode 100644 index 0000000..e9b44cc --- /dev/null +++ b/tests/test_evaluator_alignment_qa.py @@ -0,0 +1,20 @@ +from pathlib import Path +from didactopus.evaluator_alignment_qa import evaluator_alignment_for_pack + +def make_pack(base: Path, concepts_yaml: str, roadmap_yaml: str, projects_yaml: str, rubrics_yaml: str, evaluator_yaml: str): + (base / "pack.yaml").write_text("name: p\ndisplay_name: P\nversion: 0.1.0\n", encoding="utf-8") + (base / "concepts.yaml").write_text(concepts_yaml, encoding="utf-8") + (base / "roadmap.yaml").write_text(roadmap_yaml, encoding="utf-8") + (base / "projects.yaml").write_text(projects_yaml, encoding="utf-8") + (base / "rubrics.yaml").write_text(rubrics_yaml, encoding="utf-8") + (base / "evaluator.yaml").write_text(evaluator_yaml, encoding="utf-8") + +def test_detects_uncovered_mastery_signals(tmp_path: Path) -> None: + make_pack(tmp_path, + "concepts:\n - id: c1\n title: Foundations\n description: enough description here\n mastery_signals: [Explain foundations clearly]\n", + "stages:\n - id: s1\n title: One\n concepts: [c1]\n checkpoint: [Explain foundations]\n", + "projects:\n - id: p1\n title: Project\n prerequisites: [c1]\n deliverables: [report]\n", + "rubrics:\n - id: r1\n title: Basic\n criteria: [correctness]\n", + "dimensions:\n - name: typography\n description: page polish\n") + result = evaluator_alignment_for_pack(tmp_path) + assert any('Mastery signal' in w for w in result['warnings']) diff --git a/tests/test_evidence_flow_ledger_qa.py b/tests/test_evidence_flow_ledger_qa.py new file mode 100644 index 0000000..787e99b --- /dev/null +++ b/tests/test_evidence_flow_ledger_qa.py @@ -0,0 +1,24 @@ +from pathlib import Path +from didactopus.evidence_flow_ledger_qa import evidence_flow_ledger_for_pack + +def make_pack(base: Path, concepts_yaml: str, roadmap_yaml: str, projects_yaml: str, rubrics_yaml: str, evaluator_yaml: str, ledger_yaml: str): + (base / "pack.yaml").write_text("name: p\ndisplay_name: P\nversion: 0.1.0\n", encoding="utf-8") + (base / "concepts.yaml").write_text(concepts_yaml, encoding="utf-8") + (base / "roadmap.yaml").write_text(roadmap_yaml, encoding="utf-8") + (base / "projects.yaml").write_text(projects_yaml, encoding="utf-8") + (base / "rubrics.yaml").write_text(rubrics_yaml, encoding="utf-8") + (base / "evaluator.yaml").write_text(evaluator_yaml, encoding="utf-8") + (base / "mastery_ledger.yaml").write_text(ledger_yaml, encoding="utf-8") + +def test_detects_missing_dimension_mapping(tmp_path: Path) -> None: + make_pack( + tmp_path, + "concepts:\n - id: c1\n title: Foundations\n description: enough description here\n mastery_signals: [Explain foundations clearly]\n", + "stages:\n - id: s1\n title: One\n concepts: [c1]\n checkpoint: [explanation exercise]\n", + "projects:\n - id: p1\n title: Project\n prerequisites: [c1]\n deliverables: [explanation]\n", + "rubrics:\n - id: r1\n title: Basic\n criteria: [correctness]\n", + "dimensions:\n - name: explanation\n description: quality of explanation\n", + "entry_schema:\n concept_id: str\n score: float\n confidence: float\n last_updated: datetime\n", + ) + result = evidence_flow_ledger_for_pack(tmp_path) + assert any("dimension" in w.lower() for w in result["warnings"]) diff --git a/tests/test_files.py b/tests/test_files.py new file mode 100644 index 0000000..1e2d179 --- /dev/null +++ b/tests/test_files.py @@ -0,0 +1,7 @@ +from pathlib import Path + +def test_scaffold_files_exist(): + assert Path("src/didactopus/api.py").exists() + assert Path("src/didactopus/worker.py").exists() + assert Path("docker-compose.yml").exists() + assert Path("webui/src/App.jsx").exists() diff --git a/tests/test_frontend_files.py b/tests/test_frontend_files.py new file mode 100644 index 0000000..9048037 --- /dev/null +++ b/tests/test_frontend_files.py @@ -0,0 +1,5 @@ +from pathlib import Path + +def test_frontend_scaffold_exists(): + assert Path("webui/src/App.jsx").exists() + assert Path("webui/src/api.js").exists() diff --git a/tests/test_graph_qa.py b/tests/test_graph_qa.py new file mode 100644 index 0000000..fd82c91 --- /dev/null +++ b/tests/test_graph_qa.py @@ -0,0 +1,28 @@ +from pathlib import Path +from didactopus.graph_qa import graph_qa_for_pack + +def make_pack(base: Path, concepts_yaml: str, roadmap_yaml: str): + (base / "pack.yaml").write_text("name: p\ndisplay_name: P\nversion: 0.1.0\n", encoding="utf-8") + (base / "concepts.yaml").write_text(concepts_yaml, encoding="utf-8") + (base / "roadmap.yaml").write_text(roadmap_yaml, encoding="utf-8") + (base / "projects.yaml").write_text("projects: []\n", encoding="utf-8") + (base / "rubrics.yaml").write_text("rubrics: []\n", encoding="utf-8") + +def test_cycle_and_isolated_detected(tmp_path: Path) -> None: + make_pack( + tmp_path, + "concepts:\n - id: a\n title: A\n description: desc enough here\n prerequisites: [c]\n - id: b\n title: B\n description: desc enough here\n prerequisites: [a]\n - id: c\n title: C\n description: desc enough here\n prerequisites: [b]\n - id: iso\n title: Iso\n description: isolated description\n prerequisites: []\n", + "stages:\n - id: s1\n title: One\n concepts: [a,b,c,iso]\n", + ) + result = graph_qa_for_pack(tmp_path) + assert any("cycle" in w.lower() for w in result["warnings"]) + assert any("isolated" in w.lower() for w in result["warnings"]) + +def test_bottleneck_detected(tmp_path: Path) -> None: + make_pack( + tmp_path, + "concepts:\n - id: core\n title: Core\n description: central desc enough\n prerequisites: []\n - id: d1\n title: D1\n description: desc enough here\n prerequisites: [core]\n - id: d2\n title: D2\n description: desc enough here\n prerequisites: [core]\n - id: d3\n title: D3\n description: desc enough here\n prerequisites: [core]\n", + "stages:\n - id: s1\n title: One\n concepts: [core,d1,d2,d3]\n", + ) + result = graph_qa_for_pack(tmp_path) + assert any("bottleneck" in w.lower() for w in result["warnings"]) diff --git a/tests/test_import_validator.py b/tests/test_import_validator.py new file mode 100644 index 0000000..758607f --- /dev/null +++ b/tests/test_import_validator.py @@ -0,0 +1,14 @@ +from pathlib import Path +from didactopus.import_validator import preview_draft_pack_import + +def test_preview_includes_semantic_warnings(tmp_path: Path) -> None: + (tmp_path / "pack.yaml").write_text("name: p\ndisplay_name: P\nversion: 0.1.0\n", encoding="utf-8") + (tmp_path / "concepts.yaml").write_text( + "concepts:\n - id: c1\n title: Prior and Posterior\n description: Beliefs before and after evidence.\n - id: c2\n title: Posterior Analysis\n description: Beliefs before and after evidence.\n", + encoding="utf-8" + ) + (tmp_path / "roadmap.yaml").write_text("stages:\n - id: s1\n title: Foundations\n concepts: [c1]\n - id: s2\n title: Advanced Inference\n concepts: [c2]\n", encoding="utf-8") + (tmp_path / "projects.yaml").write_text("projects: []\n", encoding="utf-8") + (tmp_path / "rubrics.yaml").write_text("rubrics: []\n", encoding="utf-8") + preview = preview_draft_pack_import(tmp_path, "ws1") + assert isinstance(preview.semantic_warnings, list) diff --git a/tests/test_onboarding.py b/tests/test_onboarding.py new file mode 100644 index 0000000..1280095 --- /dev/null +++ b/tests/test_onboarding.py @@ -0,0 +1,10 @@ +from didactopus.orchestration_models import LearnerProfile +from didactopus.onboarding import build_initial_run_state, build_first_session_plan + +def test_first_session_plan_has_clear_next_action(): + profile = LearnerProfile(learner_id="u1", preferred_session_minutes=20) + state = build_initial_run_state(profile) + plan = build_first_session_plan(profile, [{"id": "c1", "title": "Foundations"}]) + assert state.status == "onboarding" + assert "Foundations" in plan.headline + assert len(plan.tasks) >= 1 diff --git a/tests/test_orchestrator.py b/tests/test_orchestrator.py new file mode 100644 index 0000000..dfba71d --- /dev/null +++ b/tests/test_orchestrator.py @@ -0,0 +1,16 @@ +from didactopus.learner_state import LearnerState, EvidenceEvent +from didactopus.progression_engine import apply_evidence +from didactopus.orchestration_models import LearnerProfile, RunState, StopCriteria +from didactopus.orchestrator import run_learning_cycle + +def test_learning_cycle_returns_recommendations(): + concepts = [ + {"id": "a", "title": "A", "prerequisites": []}, + {"id": "b", "title": "B", "prerequisites": ["a"]}, + ] + learner = LearnerState(learner_id="u1") + apply_evidence(learner, EvidenceEvent(concept_id="a", dimension="mastery", score=0.9, confidence_hint=0.9, timestamp="2026-03-13T12:00:00+00:00")) + run = RunState(profile=LearnerProfile(learner_id="u1")) + crit = StopCriteria(min_mastered_concepts=10, min_average_score=0.8, min_average_confidence=0.7) + result = run_learning_cycle(learner, run, concepts, crit) + assert "recommendation_cards" in result diff --git a/tests/test_pack_export.py b/tests/test_pack_export.py new file mode 100644 index 0000000..85670f5 --- /dev/null +++ b/tests/test_pack_export.py @@ -0,0 +1,17 @@ +from pathlib import Path +from didactopus.pack_to_frontend import convert_pack + +def test_convert_pack(tmp_path: Path): + (tmp_path / "pack.yaml").write_text( + "name: p1\ndisplay_name: Pack 1\ndescription: Demo pack\n", encoding="utf-8" + ) + (tmp_path / "concepts.yaml").write_text( + "concepts:\n - id: c1\n title: Concept 1\n prerequisites: []\n", encoding="utf-8" + ) + (tmp_path / "pack_compliance_manifest.json").write_text( + '{"derived_from_sources":["s1"],"attribution_required":true,"share_alike_required":false,"noncommercial_only":false,"restrictive_flags":[]}', + encoding="utf-8" + ) + payload = convert_pack(tmp_path) + assert payload["id"] == "p1" + assert payload["concepts"][0]["id"] == "c1" diff --git a/tests/test_pack_validator.py b/tests/test_pack_validator.py new file mode 100644 index 0000000..82c5d40 --- /dev/null +++ b/tests/test_pack_validator.py @@ -0,0 +1,11 @@ +from pathlib import Path +from didactopus.pack_validator import validate_pack_directory + +def test_valid_pack(tmp_path: Path) -> None: + (tmp_path / "pack.yaml").write_text("name: p\ndisplay_name: P\nversion: 0.1.0\n", encoding="utf-8") + (tmp_path / "concepts.yaml").write_text("concepts:\n - id: c1\n title: C1\n description: A full enough description.\n", encoding="utf-8") + (tmp_path / "roadmap.yaml").write_text("stages:\n - id: s1\n title: S1\n concepts: [c1]\n", encoding="utf-8") + (tmp_path / "projects.yaml").write_text("projects:\n - id: p1\n title: P1\n prerequisites: [c1]\n", encoding="utf-8") + (tmp_path / "rubrics.yaml").write_text("rubrics:\n - id: r1\n title: R1\n criteria: [correctness]\n", encoding="utf-8") + result = validate_pack_directory(tmp_path) + assert result["ok"] is True diff --git a/tests/test_path_quality_qa.py b/tests/test_path_quality_qa.py new file mode 100644 index 0000000..dc3724b --- /dev/null +++ b/tests/test_path_quality_qa.py @@ -0,0 +1,20 @@ +from pathlib import Path +from didactopus.path_quality_qa import path_quality_for_pack + +def make_pack(base: Path, concepts_yaml: str, roadmap_yaml: str, projects_yaml: str): + (base / "pack.yaml").write_text("name: p\ndisplay_name: P\nversion: 0.1.0\n", encoding="utf-8") + (base / "concepts.yaml").write_text(concepts_yaml, encoding="utf-8") + (base / "roadmap.yaml").write_text(roadmap_yaml, encoding="utf-8") + (base / "projects.yaml").write_text(projects_yaml, encoding="utf-8") + (base / "rubrics.yaml").write_text("rubrics: []\n", encoding="utf-8") + +def test_detects_checkpoint_and_unassessed_issues(tmp_path: Path) -> None: + make_pack( + tmp_path, + "concepts:\n - id: c1\n title: Foundations\n description: foundations description enough\n prerequisites: []\n - id: c2\n title: Advanced Inference\n description: advanced inference description enough\n prerequisites: [c1]\n", + "stages:\n - id: s1\n title: One\n concepts: [c1,c2]\n checkpoint: []\n", + "projects: []\n", + ) + result = path_quality_for_pack(tmp_path) + assert any("no checkpoint" in w.lower() for w in result["warnings"]) + assert any("not visibly assessed" in w.lower() for w in result["warnings"]) diff --git a/tests/test_progression_engine.py b/tests/test_progression_engine.py new file mode 100644 index 0000000..346a1bc --- /dev/null +++ b/tests/test_progression_engine.py @@ -0,0 +1,32 @@ +from didactopus.learner_state import LearnerState, EvidenceEvent +from didactopus.progression_engine import apply_evidence, decay_confidence + +def test_apply_evidence_creates_record(): + state = LearnerState(learner_id="u1") + event = EvidenceEvent( + concept_id="c1", + dimension="mastery", + score=0.8, + confidence_hint=0.7, + timestamp="2026-03-13T12:00:00+00:00", + ) + apply_evidence(state, event) + rec = state.get_record("c1", "mastery") + assert rec is not None + assert rec.evidence_count == 1 + assert rec.score > 0 + +def test_decay_confidence_reduces_confidence(): + state = LearnerState(learner_id="u1") + event = EvidenceEvent( + concept_id="c1", + dimension="mastery", + score=0.9, + confidence_hint=0.9, + timestamp="2026-01-01T12:00:00+00:00", + ) + apply_evidence(state, event) + before = state.get_record("c1", "mastery").confidence + decay_confidence(state, "2026-03-13T12:00:00+00:00") + after = state.get_record("c1", "mastery").confidence + assert after < before diff --git a/tests/test_python_scaffold.py b/tests/test_python_scaffold.py new file mode 100644 index 0000000..9d20450 --- /dev/null +++ b/tests/test_python_scaffold.py @@ -0,0 +1,5 @@ +from pathlib import Path + +def test_python_scaffold_exists(): + assert Path("src/didactopus/learner_state.py").exists() + assert Path("src/didactopus/progression_engine.py").exists() diff --git a/tests/test_readiness.py b/tests/test_readiness.py new file mode 100644 index 0000000..c8079e1 --- /dev/null +++ b/tests/test_readiness.py @@ -0,0 +1,15 @@ +from didactopus.learner_state import LearnerState, EvidenceEvent +from didactopus.progression_engine import apply_evidence +from didactopus.readiness import concept_ready + +def test_concept_ready_true_when_prereq_met(): + state = LearnerState(learner_id="u1") + apply_evidence(state, EvidenceEvent( + concept_id="p1", dimension="mastery", score=0.9, confidence_hint=0.9, + timestamp="2026-03-13T12:00:00+00:00" + )) + assert concept_ready(state, "c2", ["p1"], min_score=0.5, min_confidence=0.2) + +def test_concept_ready_false_when_prereq_missing(): + state = LearnerState(learner_id="u1") + assert not concept_ready(state, "c2", ["p1"], min_score=0.5, min_confidence=0.2) diff --git a/tests/test_recommendations.py b/tests/test_recommendations.py new file mode 100644 index 0000000..57ea301 --- /dev/null +++ b/tests/test_recommendations.py @@ -0,0 +1,25 @@ +from didactopus.learner_state import LearnerState, EvidenceEvent +from didactopus.progression_engine import apply_evidence +from didactopus.recommendations import recommend_next_concepts, recommend_reinforcement_targets + +def test_recommend_next_concepts(): + concepts = [ + {"id": "a", "title": "A", "prerequisites": []}, + {"id": "b", "title": "B", "prerequisites": ["a"]}, + ] + state = LearnerState(learner_id="u1") + apply_evidence(state, EvidenceEvent( + concept_id="a", dimension="mastery", score=0.9, confidence_hint=0.9, + timestamp="2026-03-13T12:00:00+00:00" + )) + recs = recommend_next_concepts(state, concepts, min_score=0.5, min_confidence=0.2) + assert any(r["concept_id"] == "b" for r in recs) + +def test_recommend_reinforcement_targets(): + state = LearnerState(learner_id="u1") + apply_evidence(state, EvidenceEvent( + concept_id="a", dimension="mastery", score=0.3, confidence_hint=0.1, + timestamp="2026-03-13T12:00:00+00:00" + )) + recs = recommend_reinforcement_targets(state, low_confidence_threshold=0.5) + assert any(r["concept_id"] == "a" for r in recs) diff --git a/tests/test_review_bridge.py b/tests/test_review_bridge.py new file mode 100644 index 0000000..0e70984 --- /dev/null +++ b/tests/test_review_bridge.py @@ -0,0 +1,20 @@ +from pathlib import Path +from didactopus.review_bridge import ReviewWorkspaceBridge + +def _make_workspace(base: Path): + draft = base / "draft_pack" + draft.mkdir(parents=True, exist_ok=True) + (draft / "pack.yaml").write_text("name: test\nversion: 0.1.0-draft\n", encoding="utf-8") + (draft / "concepts.yaml").write_text( + "concepts:\n - id: c1\n title: Concept One\n description: Desc\n prerequisites: []\n mastery_signals: []\n", + encoding="utf-8", + ) + (draft / "conflict_report.md").write_text("# Conflict Report\n\n- One conflict\n", encoding="utf-8") + +def test_bridge_load_and_save(tmp_path: Path) -> None: + _make_workspace(tmp_path) + bridge = ReviewWorkspaceBridge(tmp_path, reviewer="R") + session = bridge.load_session() + assert session.reviewer == "R" + bridge.save_session(session) + assert (tmp_path / "review_session.json").exists() diff --git a/tests/test_scaffold_files.py b/tests/test_scaffold_files.py new file mode 100644 index 0000000..9b11e28 --- /dev/null +++ b/tests/test_scaffold_files.py @@ -0,0 +1,7 @@ +from pathlib import Path + +def test_scaffold_files_exist(): + assert Path("src/didactopus/api.py").exists() + assert Path("src/didactopus/repository.py").exists() + assert Path("src/didactopus/synthesis.py").exists() + assert Path("webui/src/App.jsx").exists() diff --git a/tests/test_semantic_qa.py b/tests/test_semantic_qa.py new file mode 100644 index 0000000..cdc05ab --- /dev/null +++ b/tests/test_semantic_qa.py @@ -0,0 +1,27 @@ +from pathlib import Path +from didactopus.semantic_qa import semantic_qa_for_pack + +def make_pack(base: Path, concepts_yaml: str, roadmap_yaml: str): + (base / "pack.yaml").write_text("name: p\ndisplay_name: P\nversion: 0.1.0\n", encoding="utf-8") + (base / "concepts.yaml").write_text(concepts_yaml, encoding="utf-8") + (base / "roadmap.yaml").write_text(roadmap_yaml, encoding="utf-8") + (base / "projects.yaml").write_text("projects: []\n", encoding="utf-8") + (base / "rubrics.yaml").write_text("rubrics: []\n", encoding="utf-8") + +def test_semantic_warnings_detected(tmp_path: Path) -> None: + make_pack( + tmp_path, + "concepts:\n - id: c1\n title: Prior and Posterior\n description: Beliefs before and after evidence.\n - id: c2\n title: Posterior Analysis\n description: Beliefs before and after evidence.\n", + "stages:\n - id: s1\n title: Foundations\n concepts: [c1]\n - id: s2\n title: Advanced Inference\n concepts: [c2]\n", + ) + result = semantic_qa_for_pack(tmp_path) + assert len(result["warnings"]) >= 1 + +def test_semantic_summary_exists(tmp_path: Path) -> None: + make_pack( + tmp_path, + "concepts:\n - id: c1\n title: Bayes Prior\n description: Prior beliefs before evidence in a probabilistic model.\n", + "stages:\n - id: s1\n title: Prior Beliefs\n concepts: [c1]\n", + ) + result = semantic_qa_for_pack(tmp_path) + assert "semantic_warning_count" in result["summary"] diff --git a/tests/test_stop_criteria.py b/tests/test_stop_criteria.py new file mode 100644 index 0000000..2dd7f10 --- /dev/null +++ b/tests/test_stop_criteria.py @@ -0,0 +1,13 @@ +from didactopus.learner_state import LearnerState, MasteryRecord +from didactopus.orchestration_models import LearnerProfile, RunState, StopCriteria +from didactopus.stop_criteria import assess_claim_readiness + +def test_claim_readiness_true_when_threshold_met(): + learner = LearnerState( + learner_id="u1", + records=[MasteryRecord(concept_id="c1", dimension="mastery", score=0.9, confidence=0.8, evidence_count=3, last_updated="2026-03-13T12:00:00+00:00")] + ) + run = RunState(profile=LearnerProfile(learner_id="u1")) + crit = StopCriteria(min_mastered_concepts=1, min_average_score=0.7, min_average_confidence=0.6) + result = assess_claim_readiness(learner, run, crit) + assert result["ready"] is True diff --git a/tests/test_ui_files.py b/tests/test_ui_files.py new file mode 100644 index 0000000..7a06f82 --- /dev/null +++ b/tests/test_ui_files.py @@ -0,0 +1,6 @@ +from pathlib import Path + +def test_ui_files_exist(): + assert Path("webui/src/App.jsx").exists() + assert Path("webui/src/storage.js").exists() + assert Path("webui/public/packs/bayes-pack.json").exists() diff --git a/tests/test_ui_scaffold.py b/tests/test_ui_scaffold.py index b0b2e29..a2164b2 100644 --- a/tests/test_ui_scaffold.py +++ b/tests/test_ui_scaffold.py @@ -1,7 +1,5 @@ from pathlib import Path -from didactopus.ui_scaffold import write_review_ui - -def test_write_ui(tmp_path: Path) -> None: - write_review_ui(tmp_path) - assert (tmp_path / "index.html").exists() +def test_ui_files_exist(): + assert Path("webui/src/App.jsx").exists() + assert Path("webui/src/sampleData.js").exists() diff --git a/tests/test_webui_files.py b/tests/test_webui_files.py index ef6e13e..c0e0c6e 100644 --- a/tests/test_webui_files.py +++ b/tests/test_webui_files.py @@ -1,6 +1,5 @@ from pathlib import Path - def test_webui_scaffold_exists() -> None: assert Path("webui/src/App.jsx").exists() - assert Path("webui/sample/review_data.json").exists() + assert Path("webui/package.json").exists() diff --git a/tests/test_workspace_manager.py b/tests/test_workspace_manager.py new file mode 100644 index 0000000..05091e0 --- /dev/null +++ b/tests/test_workspace_manager.py @@ -0,0 +1,17 @@ +from pathlib import Path +from didactopus.workspace_manager import WorkspaceManager + +def test_create_and_get_workspace(tmp_path: Path) -> None: + mgr = WorkspaceManager(tmp_path / "registry.json", tmp_path / "roots") + meta = mgr.create_workspace("ws1", "Workspace One") + assert meta.workspace_id == "ws1" + got = mgr.get_workspace("ws1") + assert got is not None + assert got.title == "Workspace One" + +def test_recent_tracking(tmp_path: Path) -> None: + mgr = WorkspaceManager(tmp_path / "registry.json", tmp_path / "roots") + mgr.create_workspace("ws1", "Workspace One") + mgr.touch_recent("ws1") + reg = mgr.list_workspaces() + assert reg.recent_workspace_ids[0] == "ws1" diff --git a/webui/public/packs/bayes-pack.json b/webui/public/packs/bayes-pack.json new file mode 100644 index 0000000..f0de85f --- /dev/null +++ b/webui/public/packs/bayes-pack.json @@ -0,0 +1,53 @@ +{ + "id": "bayes-pack", + "title": "Bayesian Reasoning", + "subtitle": "Probability, evidence, updating, and model criticism.", + "level": "novice-friendly", + "concepts": [ + { + "id": "prior", + "title": "Prior", + "prerequisites": [], + "masteryDimension": "mastery", + "exerciseReward": "Prior badge earned" + }, + { + "id": "posterior", + "title": "Posterior", + "prerequisites": [ + "prior" + ], + "masteryDimension": "mastery", + "exerciseReward": "Posterior path opened" + }, + { + "id": "model-checking", + "title": "Model Checking", + "prerequisites": [ + "posterior" + ], + "masteryDimension": "mastery", + "exerciseReward": "Model-checking unlocked" + } + ], + "onboarding": { + "headline": "Start with a fast visible win", + "body": "Read one short orientation, answer one guided question, and leave with your first mastery marker.", + "checklist": [ + "Read the one-screen topic orientation", + "Answer one guided exercise", + "Write one explanation in your own words" + ] + }, + "compliance": { + "sources": 2, + "attributionRequired": true, + "shareAlikeRequired": true, + "noncommercialOnly": true, + "flags": [ + "share-alike", + "noncommercial", + "excluded-third-party-content" + ] + } +} \ No newline at end of file diff --git a/webui/public/packs/stats-pack.json b/webui/public/packs/stats-pack.json new file mode 100644 index 0000000..7ccf0fc --- /dev/null +++ b/webui/public/packs/stats-pack.json @@ -0,0 +1,49 @@ +{ + "id": "stats-pack", + "title": "Introductory Statistics", + "subtitle": "Descriptive statistics, sampling, and inference.", + "level": "novice-friendly", + "concepts": [ + { + "id": "descriptive", + "title": "Descriptive Statistics", + "prerequisites": [], + "masteryDimension": "mastery", + "exerciseReward": "Descriptive tools unlocked" + }, + { + "id": "sampling", + "title": "Sampling", + "prerequisites": [ + "descriptive" + ], + "masteryDimension": "mastery", + "exerciseReward": "Sampling pathway opened" + }, + { + "id": "inference", + "title": "Inference", + "prerequisites": [ + "sampling" + ], + "masteryDimension": "mastery", + "exerciseReward": "Inference challenge unlocked" + } + ], + "onboarding": { + "headline": "Build your first useful data skill", + "body": "You will learn one concept that immediately helps you summarize real data.", + "checklist": [ + "See one worked example", + "Compute one short example yourself", + "Explain what the result means" + ] + }, + "compliance": { + "sources": 1, + "attributionRequired": true, + "shareAlikeRequired": false, + "noncommercialOnly": false, + "flags": [] + } +} \ No newline at end of file diff --git a/webui/src/App.jsx b/webui/src/App.jsx index ac051de..07569b6 100644 --- a/webui/src/App.jsx +++ b/webui/src/App.jsx @@ -1,254 +1,203 @@ -import React, { useMemo, useState } from "react"; -import reviewData from "../sample/review_data.json"; +import React, { useEffect, useMemo, useState } from "react"; +const API = "http://127.0.0.1:8765"; const statuses = ["needs_review", "trusted", "provisional", "rejected"]; -function downloadJson(filename, data) { - const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" }); - const url = URL.createObjectURL(blob); - const a = document.createElement("a"); - a.href = url; - a.download = filename; - a.click(); - URL.revokeObjectURL(url); -} - -function promotedPackFromState(state) { - return { - pack: { - ...state.pack, - version: String(state.pack.version || "0.1.0-draft").replace("-draft", "-reviewed"), - curation: { - reviewer: state.reviewer, - ledger_entries: state.ledger.length - } - }, - concepts: state.concepts - .filter((c) => c.status !== "rejected") - .map((c) => ({ - id: c.concept_id, - title: c.title, - description: c.description, - prerequisites: c.prerequisites, - mastery_signals: c.mastery_signals, - status: c.status, - notes: c.notes, - mastery_profile: {} - })), - conflicts: state.conflicts, - review_flags: state.review_flags - }; -} - export default function App() { - const [state, setState] = useState(reviewData); - const [selectedId, setSelectedId] = useState(reviewData.concepts[0]?.concept_id || ""); - const selected = useMemo( - () => state.concepts.find((c) => c.concept_id === selectedId) || null, - [state, selectedId] - ); + const [registry, setRegistry] = useState({ workspaces: [], recent_workspace_ids: [] }); + const [workspaceId, setWorkspaceId] = useState(""); + const [workspaceTitle, setWorkspaceTitle] = useState(""); + const [session, setSession] = useState(null); + const [selectedId, setSelectedId] = useState(""); + const [pendingActions, setPendingActions] = useState([]); + const [message, setMessage] = useState("Connecting to local Didactopus bridge..."); - function updateConcept(conceptId, patch, rationale) { - setState((prev) => { - const concepts = prev.concepts.map((c) => - c.concept_id === conceptId ? { ...c, ...patch } : c - ); - const ledger = [ - ...prev.ledger, - { - reviewer: prev.reviewer, - action: { - action_type: "note", - target: conceptId, - payload: patch, - rationale: rationale || "UI edit" - } - } - ]; - return { ...prev, concepts, ledger }; + async function loadRegistry() { + const res = await fetch(`${API}/api/workspaces`); + const data = await res.json(); + setRegistry(data); + if (data.recent_workspace_ids?.length && !session) { + setMessage("Choose or reopen a workspace."); + } + } + + useEffect(() => { + loadRegistry().catch(() => setMessage("Could not connect to local review bridge. Start the Python bridge service first.")); + }, []); + + async function createWorkspace() { + if (!workspaceId || !workspaceTitle) return; + await fetch(`${API}/api/workspaces/create`, { + method: "POST", + headers: {"Content-Type": "application/json"}, + body: JSON.stringify({ workspace_id: workspaceId, title: workspaceTitle }) }); + await loadRegistry(); + await openWorkspace(workspaceId); + } + + async function openWorkspace(id) { + const res = await fetch(`${API}/api/workspaces/open`, { + method: "POST", + headers: {"Content-Type": "application/json"}, + body: JSON.stringify({ workspace_id: id }) + }); + const opened = await res.json(); + if (!opened.ok) { + setMessage("Could not open workspace."); + return; + } + const sessionRes = await fetch(`${API}/api/load`); + const sessionData = await sessionRes.json(); + setSession(sessionData.session); + setSelectedId(sessionData.session?.draft_pack?.concepts?.[0]?.concept_id || ""); + setPendingActions([]); + setMessage(`Opened workspace ${id}.`); + await loadRegistry(); + } + + const selected = useMemo(() => { + if (!session) return null; + return session.draft_pack.concepts.find((c) => c.concept_id === selectedId) || null; + }, [session, selectedId]); + + function queueAction(action) { + setPendingActions((prev) => [...prev, action]); + } + + function patchConcept(conceptId, patch, rationale) { + if (!session) return; + const concepts = session.draft_pack.concepts.map((c) => + c.concept_id === conceptId ? { ...c, ...patch } : c + ); + setSession({ ...session, draft_pack: { ...session.draft_pack, concepts } }); + + if (patch.status !== undefined) queueAction({ action_type: "set_status", target: conceptId, payload: { status: patch.status }, rationale }); + if (patch.title !== undefined) queueAction({ action_type: "edit_title", target: conceptId, payload: { title: patch.title }, rationale }); + if (patch.description !== undefined) queueAction({ action_type: "edit_description", target: conceptId, payload: { description: patch.description }, rationale }); + if (patch.prerequisites !== undefined) queueAction({ action_type: "edit_prerequisites", target: conceptId, payload: { prerequisites: patch.prerequisites }, rationale }); + if (patch.notes !== undefined) queueAction({ action_type: "edit_notes", target: conceptId, payload: { notes: patch.notes }, rationale }); } function resolveConflict(conflict) { - setState((prev) => ({ - ...prev, - conflicts: prev.conflicts.filter((c) => c !== conflict), - ledger: [ - ...prev.ledger, - { - reviewer: prev.reviewer, - action: { - action_type: "resolve_conflict", - target: "", - payload: { conflict }, - rationale: "Resolved in UI" - } - } - ] - })); + if (!session) return; + setSession({ + ...session, + draft_pack: { ...session.draft_pack, conflicts: session.draft_pack.conflicts.filter((c) => c !== conflict) } + }); + queueAction({ action_type: "resolve_conflict", target: "", payload: { conflict }, rationale: "Resolved in UI" }); } - const promoted = promotedPackFromState(state); + async function saveChanges() { + if (!pendingActions.length) { + setMessage("No pending changes to save."); + return; + } + const res = await fetch(`${API}/api/save`, { + method: "POST", + headers: {"Content-Type": "application/json"}, + body: JSON.stringify({ actions: pendingActions }) + }); + const data = await res.json(); + setSession(data.session); + setPendingActions([]); + setMessage("Saved review state."); + } + + async function exportPromoted() { + const res = await fetch(`${API}/api/export`, { + method: "POST", + headers: {"Content-Type": "application/json"}, + body: JSON.stringify({}) + }); + const data = await res.json(); + setMessage(`Exported promoted pack to ${data.promoted_pack_dir}`); + } return (
-

Didactopus Review UI

+

Didactopus Workspace Manager

- Reduce the activation-energy hump: move from raw course-derived draft pack - to curated reviewed domain pack with less friction. + Reduce the activation-energy hump from raw course material to reviewed domain pack + by organizing draft-pack curation as a manageable local workflow.

+
{message}
- - + +
-

Pack

-
{state.pack.display_name || state.pack.name}
-
Reviewer: {state.reviewer}
-
Concepts: {state.concepts.length}
+

Create Workspace

+ + +
-

Conflicts

-
{state.conflicts.length}
+

Recent

+
    {registry.recent_workspace_ids.map((id) =>
  • )}
-

Flags

-
{state.review_flags.length}
+

All Workspaces

+
    {registry.workspaces.map((ws) =>
  • )}
-

Ledger

-
{state.ledger.length}
+

Pending Actions

+
{pendingActions.length}
-
- + {session && ( +
+ -
- {selected ? ( - <> +
+ {selected && (

Concept Editor

- - + -