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}
- downloadJson("review_data.edited.json", state)}>Export Review State
- downloadJson("promoted_pack.json", promoted)}>Export Promoted Pack
+ Save Review State
+ Export Promoted Pack
-
Pack
-
{state.pack.display_name || state.pack.name}
-
Reviewer: {state.reviewer}
-
Concepts: {state.concepts.length}
+
Create Workspace
+
Workspace ID setWorkspaceId(e.target.value)} />
+
Title setWorkspaceTitle(e.target.value)} />
+
Create
-
Conflicts
-
{state.conflicts.length}
+
Recent
+
{registry.recent_workspace_ids.map((id) => openWorkspace(id)}>{id} )}
-
Flags
-
{state.review_flags.length}
+
All Workspaces
+
{registry.workspaces.map((ws) => openWorkspace(ws.workspace_id)}>{ws.title} ({ws.workspace_id}) )}
-
Ledger
-
{state.ledger.length}
+
Pending Actions
+
{pendingActions.length}
-
-
- Concepts
- {state.concepts.map((c) => (
- setSelectedId(c.concept_id)}
- >
- {c.title}
- {c.status}
-
- ))}
-
+ {session && (
+
+
+ Concepts
+ {session.draft_pack.concepts.map((c) => (
+ setSelectedId(c.concept_id)}>
+ {c.title}
+ {c.status}
+
+ ))}
+
-
- {selected ? (
- <>
+
+ {selected && (
Concept Editor
-
- Title
- updateConcept(selected.concept_id, { title: e.target.value }, "Edited title")}
- />
-
-
- Status
- updateConcept(selected.concept_id, { status: e.target.value }, "Changed trust status")}
- >
- {statuses.map((s) => (
- {s}
- ))}
+ Title patchConcept(selected.concept_id, { title: e.target.value }, "Edited title")} />
+ Status
+ patchConcept(selected.concept_id, { status: e.target.value }, "Changed trust status")}>
+ {statuses.map((s) => {s} )}
-
- Description
-
-
- Prerequisites (comma-separated ids)
-
- updateConcept(
- selected.concept_id,
- {
- prerequisites: e.target.value
- .split(",")
- .map((x) => x.trim())
- .filter(Boolean)
- },
- "Edited prerequisites"
- )
- }
- />
-
-
- Notes
-
+ Description
+ Prerequisites (comma-separated ids) patchConcept(selected.concept_id, { prerequisites: e.target.value.split(",").map((x) => x.trim()).filter(Boolean) }, "Edited prerequisites")} />
+ Notes (one per line)
+ )}
+
-
-
Mastery Signals
-
- {(selected.mastery_signals || []).map((signal, idx) => (
- {signal}
- ))}
-
-
- >
- ) : (
- No concept selected.
- )}
-
-
-
-
-
Conflicts
- {state.conflicts.length ? state.conflicts.map((conflict, idx) => (
-
-
{conflict}
-
resolveConflict(conflict)}>Resolve
-
- )) :
No remaining conflicts.
}
-
-
-
-
Review Flags
-
- {state.review_flags.map((flag, idx) => {flag} )}
-
-
-
-
-
Why this exists
-
- Online course material can be excellent and still be hard to activate.
- Didactopus aims to reduce the setup burden from “useful but messy course content”
- to “usable reviewed learning domain.”
-
-
-
-
+
+
+
Conflicts
+ {session.draft_pack.conflicts.length ? session.draft_pack.conflicts.map((conflict, idx) => (
+
+
{conflict}
+
resolveConflict(conflict)}>Resolve
+
+ )) :
No remaining conflicts.
}
+
+
+
Review Flags
+
{session.draft_pack.review_flags.map((flag, idx) => {flag} )}
+
+
+
+ )}
);
}
diff --git a/webui/src/api.js b/webui/src/api.js
new file mode 100644
index 0000000..4d35df8
--- /dev/null
+++ b/webui/src/api.js
@@ -0,0 +1,55 @@
+const API = "http://127.0.0.1:8011/api";
+
+function authHeaders(token, json=true) {
+ const h = { Authorization: `Bearer ${token}` };
+ if (json) h["Content-Type"] = "application/json";
+ return h;
+}
+
+export async function login(username, password) {
+ const res = await fetch(`${API}/login`, { method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({ username, password })});
+ if (!res.ok) throw new Error("login failed");
+ return await res.json();
+}
+
+export async function listCandidates(token) {
+ const res = await fetch(`${API}/knowledge-candidates`, { headers: authHeaders(token, false) });
+ if (!res.ok) throw new Error("listCandidates failed");
+ return await res.json();
+}
+
+export async function createCandidate(token, payload) {
+ const res = await fetch(`${API}/knowledge-candidates`, { method: "POST", headers: authHeaders(token), body: JSON.stringify(payload) });
+ if (!res.ok) throw new Error("createCandidate failed");
+ return await res.json();
+}
+
+export async function createReview(token, candidateId, payload) {
+ const res = await fetch(`${API}/knowledge-candidates/${candidateId}/reviews`, { method: "POST", headers: authHeaders(token), body: JSON.stringify(payload) });
+ if (!res.ok) throw new Error("createReview failed");
+ return await res.json();
+}
+
+export async function promoteCandidate(token, candidateId, payload) {
+ const res = await fetch(`${API}/knowledge-candidates/${candidateId}/promote`, { method: "POST", headers: authHeaders(token), body: JSON.stringify(payload) });
+ if (!res.ok) throw new Error("promoteCandidate failed");
+ return await res.json();
+}
+
+export async function runSynthesis(token, payload) {
+ const res = await fetch(`${API}/synthesis/run`, { method: "POST", headers: authHeaders(token), body: JSON.stringify(payload) });
+ if (!res.ok) throw new Error("runSynthesis failed");
+ return await res.json();
+}
+
+export async function listSynthesisCandidates(token) {
+ const res = await fetch(`${API}/synthesis/candidates`, { headers: authHeaders(token, false) });
+ if (!res.ok) throw new Error("listSynthesisCandidates failed");
+ return await res.json();
+}
+
+export async function promoteSynthesis(token, synthesisId, payload) {
+ const res = await fetch(`${API}/synthesis/candidates/${synthesisId}/promote`, { method: "POST", headers: authHeaders(token), body: JSON.stringify(payload) });
+ if (!res.ok) throw new Error("promoteSynthesis failed");
+ return await res.json();
+}
diff --git a/webui/src/authStore.js b/webui/src/authStore.js
new file mode 100644
index 0000000..c208022
--- /dev/null
+++ b/webui/src/authStore.js
@@ -0,0 +1,4 @@
+const KEY = "didactopus-auth";
+export function loadAuth() { try { return JSON.parse(localStorage.getItem(KEY) || "null"); } catch { return null; } }
+export function saveAuth(data) { localStorage.setItem(KEY, JSON.stringify(data)); }
+export function clearAuth() { localStorage.removeItem(KEY); }
diff --git a/webui/src/domainData.js b/webui/src/domainData.js
new file mode 100644
index 0000000..e661805
--- /dev/null
+++ b/webui/src/domainData.js
@@ -0,0 +1,92 @@
+export const domains = [
+ {
+ id: "bayesian-reasoning",
+ 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"]
+ }
+ },
+ {
+ id: "intro-stats",
+ 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: []
+ }
+ }
+];
diff --git a/webui/src/engine.js b/webui/src/engine.js
new file mode 100644
index 0000000..1cb1fe2
--- /dev/null
+++ b/webui/src/engine.js
@@ -0,0 +1,139 @@
+export function cloneState(obj) {
+ return JSON.parse(JSON.stringify(obj));
+}
+
+export function getRecord(state, conceptId, dimension = "mastery") {
+ return state.records.find((r) => r.concept_id === conceptId && r.dimension === dimension) || null;
+}
+
+export function applyEvidence(state, event, decay = 0.05, reinforcement = 0.25) {
+ const next = cloneState(state);
+ let rec = getRecord(next, event.concept_id, event.dimension);
+ if (!rec) {
+ rec = {
+ concept_id: event.concept_id,
+ dimension: event.dimension,
+ score: 0,
+ confidence: 0,
+ evidence_count: 0,
+ last_updated: event.timestamp
+ };
+ next.records.push(rec);
+ }
+
+ const weight = Math.max(0.05, Math.min(1.0, event.confidence_hint ?? 0.5));
+ rec.score = ((rec.score * rec.evidence_count) + (event.score * weight)) / Math.max(1, rec.evidence_count + 1);
+ rec.confidence = Math.min(
+ 1.0,
+ Math.max(0.0, rec.confidence * (1.0 - decay) + reinforcement * weight + 0.10 * Math.max(0.0, Math.min(1.0, event.score)))
+ );
+ rec.evidence_count += 1;
+ rec.last_updated = event.timestamp;
+ next.history.push(event);
+ return next;
+}
+
+export function prereqsSatisfied(state, concept, minScore = 0.65, minConfidence = 0.45) {
+ return (concept.prerequisites || []).every((pid) => {
+ const rec = getRecord(state, pid, concept.masteryDimension || "mastery");
+ return rec && rec.score >= minScore && rec.confidence >= minConfidence;
+ });
+}
+
+export function conceptStatus(state, concept, minScore = 0.65, minConfidence = 0.45) {
+ const rec = getRecord(state, concept.id, concept.masteryDimension || "mastery");
+ if (rec && rec.score >= minScore && rec.confidence >= minConfidence) return "mastered";
+ if (prereqsSatisfied(state, concept, minScore, minConfidence)) return rec ? "active" : "available";
+ return "locked";
+}
+
+export function buildMasteryMap(state, domain) {
+ return domain.concepts.map((c) => ({
+ id: c.id,
+ label: c.title,
+ status: conceptStatus(state, c)
+ }));
+}
+
+export function progressPercent(state, domain) {
+ const total = Math.max(1, domain.concepts.length);
+ const mastered = domain.concepts.filter((c) => conceptStatus(state, c) === "mastered").length;
+ return Math.round((mastered / total) * 100);
+}
+
+export function recommendNext(state, domain) {
+ const cards = [];
+ for (const concept of domain.concepts) {
+ const status = conceptStatus(state, concept);
+ const rec = getRecord(state, concept.id, concept.masteryDimension || "mastery");
+ if (status === "available" || status === "active") {
+ cards.push({
+ id: concept.id,
+ title: `Work on ${concept.title}`,
+ minutes: status === "available" ? 15 : 10,
+ reason: status === "available"
+ ? "Prerequisites are satisfied, so this is the best next unlock."
+ : "You have started this concept, but mastery is not yet secure.",
+ why: [
+ "Prerequisite check passed",
+ rec ? `Current score: ${rec.score.toFixed(2)}` : "No evidence recorded yet",
+ rec ? `Current confidence: ${rec.confidence.toFixed(2)}` : "Confidence starts after your first exercise"
+ ],
+ reward: concept.exerciseReward || `${concept.title} progress recorded`,
+ conceptId: concept.id,
+ scoreHint: status === "available" ? 0.82 : 0.76,
+ confidenceHint: status === "available" ? 0.72 : 0.55
+ });
+ }
+ }
+ for (const rec of state.records) {
+ if (rec.dimension === "mastery" && rec.confidence < 0.40) {
+ const concept = domain.concepts.find((c) => c.id === rec.concept_id);
+ if (concept) {
+ cards.push({
+ id: `${concept.id}-reinforce`,
+ title: `Reinforce ${concept.title}`,
+ minutes: 8,
+ reason: "Your score is promising, but confidence is still thin.",
+ why: [
+ `Confidence ${rec.confidence.toFixed(2)} is below reinforcement threshold`,
+ "A small fresh exercise can stabilize recall"
+ ],
+ reward: "Confidence ring grows",
+ conceptId: concept.id,
+ scoreHint: Math.max(0.60, rec.score),
+ confidenceHint: 0.30
+ });
+ }
+ }
+ }
+ return cards.slice(0, 4);
+}
+
+export function milestoneMessages(state, domain) {
+ const msgs = [];
+ for (const concept of domain.concepts) {
+ const status = conceptStatus(state, concept);
+ if (status === "mastered") msgs.push(`${concept.title} mastered`);
+ }
+ if (msgs.length === 0) msgs.push("Complete your first guided exercise to earn a visible mastery marker");
+ return msgs;
+}
+
+export function claimReadiness(state, domain, minScore = 0.75, minConfidence = 0.60) {
+ const mastered = domain.concepts.filter((c) => {
+ const rec = getRecord(state, c.id, c.masteryDimension || "mastery");
+ return rec && rec.score >= minScore && rec.confidence >= minConfidence;
+ }).length;
+
+ const records = domain.concepts.map((c) => getRecord(state, c.id, c.masteryDimension || "mastery")).filter(Boolean);
+ const avgScore = records.length ? records.reduce((a, r) => a + r.score, 0) / records.length : 0;
+ const avgConfidence = records.length ? records.reduce((a, r) => a + r.confidence, 0) / records.length : 0;
+
+ return {
+ ready: mastered >= Math.max(1, domain.concepts.length - 1) && avgScore >= minScore && avgConfidence >= minConfidence,
+ mastered,
+ avgScore,
+ avgConfidence
+ };
+}
diff --git a/webui/src/localEngine.js b/webui/src/localEngine.js
new file mode 100644
index 0000000..f331af6
--- /dev/null
+++ b/webui/src/localEngine.js
@@ -0,0 +1,48 @@
+export function getRecord(state, conceptId, dimension = "mastery") {
+ return state.records.find((r) => r.concept_id === conceptId && r.dimension === dimension) || null;
+}
+
+export function conceptStatus(state, concept, minScore = 0.65, minConfidence = 0.45) {
+ const rec = getRecord(state, concept.id, concept.masteryDimension || "mastery");
+ if (rec && rec.score >= minScore && rec.confidence >= minConfidence) return "mastered";
+ const prereqsOk = (concept.prerequisites || []).every((pid) => {
+ const p = getRecord(state, pid, concept.masteryDimension || "mastery");
+ return p && p.score >= minScore && p.confidence >= minConfidence;
+ });
+ if (prereqsOk) return rec ? "active" : "available";
+ return "locked";
+}
+
+export function buildMasteryMap(state, domain) {
+ return domain.concepts.map((c) => ({ id: c.id, label: c.title, status: conceptStatus(state, c) }));
+}
+
+export function progressPercent(state, domain) {
+ const total = Math.max(1, domain.concepts.length);
+ const mastered = domain.concepts.filter((c) => conceptStatus(state, c) === "mastered").length;
+ return Math.round((mastered / total) * 100);
+}
+
+export function milestoneMessages(state, domain) {
+ const msgs = [];
+ for (const concept of domain.concepts) {
+ if (conceptStatus(state, concept) === "mastered") msgs.push(`${concept.title} mastered`);
+ }
+ if (msgs.length === 0) msgs.push("Complete your first guided exercise to earn a visible mastery marker");
+ return msgs;
+}
+
+export function claimReadiness(state, domain, minScore = 0.75, minConfidence = 0.60) {
+ const records = domain.concepts
+ .map((c) => getRecord(state, c.id, c.masteryDimension || "mastery"))
+ .filter(Boolean);
+ const mastered = records.filter((r) => r.score >= minScore && r.confidence >= minConfidence).length;
+ const avgScore = records.length ? records.reduce((a, r) => a + r.score, 0) / records.length : 0;
+ const avgConfidence = records.length ? records.reduce((a, r) => a + r.confidence, 0) / records.length : 0;
+ return {
+ ready: mastered >= Math.max(1, domain.concepts.length - 1) && avgScore >= minScore && avgConfidence >= minConfidence,
+ mastered,
+ avgScore,
+ avgConfidence
+ };
+}
diff --git a/webui/src/sampleData.js b/webui/src/sampleData.js
new file mode 100644
index 0000000..c0c404e
--- /dev/null
+++ b/webui/src/sampleData.js
@@ -0,0 +1,100 @@
+export const domains = [
+ {
+ id: "bayesian-reasoning",
+ title: "Bayesian Reasoning",
+ subtitle: "Probability, evidence, updating, and model criticism",
+ level: "novice-friendly",
+ rewardLabel: "First prior unlocked",
+ progress: 38,
+ milestones: ["First concept mastered", "Posterior comparison unlocked"],
+ masteryMap: [
+ { id: "prior", label: "Prior", status: "mastered" },
+ { id: "posterior", label: "Posterior", status: "active" },
+ { id: "model-checking", label: "Model Checking", status: "locked" }
+ ],
+ nextSteps: [
+ {
+ id: "compare-prior-posterior",
+ title: "Compare prior and posterior beliefs",
+ reason: "You already showed solid understanding of priors, so the next high-value step is updating beliefs with evidence.",
+ why: [
+ "Prerequisite concepts are satisfied",
+ "Your confidence on prior knowledge is above threshold",
+ "This concept unlocks model-checking later"
+ ],
+ minutes: 18,
+ reward: "Unlock model-checking pathway"
+ },
+ {
+ id: "reinforce-prior-explanation",
+ title: "Reinforce your explanation of priors",
+ reason: "Your score is good, but confidence is still a little thin.",
+ why: [
+ "Confidence estimate below reinforcement target",
+ "A short explanation exercise can stabilize recall"
+ ],
+ minutes: 8,
+ reward: "Increase confidence ring"
+ }
+ ],
+ onboarding: {
+ headline: "Start with a fast visible win",
+ body: "In your first session, you will read one short orientation, answer one guided question, and leave with your first mastery marker.",
+ checklist: [
+ "Read the one-screen 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"]
+ }
+ },
+ {
+ id: "intro-stats",
+ title: "Introductory Statistics",
+ subtitle: "Descriptive statistics, inference, and basic modeling",
+ level: "novice-friendly",
+ rewardLabel: "Variance trail opened",
+ progress: 12,
+ milestones: ["Orientation complete"],
+ masteryMap: [
+ { id: "descriptive", label: "Descriptive Stats", status: "active" },
+ { id: "sampling", label: "Sampling", status: "locked" },
+ { id: "inference", label: "Inference", status: "locked" }
+ ],
+ nextSteps: [
+ {
+ id: "descriptive-stats-basics",
+ title: "Practice mean, median, and spread",
+ reason: "This is the best low-friction entry point and gives immediate payoff.",
+ why: [
+ "No prerequisites are required",
+ "It anchors later concepts in inference"
+ ],
+ minutes: 15,
+ reward: "Unlock sampling"
+ }
+ ],
+ onboarding: {
+ headline: "Build your first useful tool",
+ 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: []
+ }
+ }
+];
diff --git a/webui/src/storage.js b/webui/src/storage.js
new file mode 100644
index 0000000..9755ec8
--- /dev/null
+++ b/webui/src/storage.js
@@ -0,0 +1,21 @@
+const KEY_PREFIX = "didactopus:learner-state:";
+
+export function loadLearnerState(domainId) {
+ const raw = localStorage.getItem(KEY_PREFIX + domainId);
+ if (!raw) {
+ return { learner_id: `learner-${domainId}`, records: [], history: [] };
+ }
+ try {
+ return JSON.parse(raw);
+ } catch {
+ return { learner_id: `learner-${domainId}`, records: [], history: [] };
+ }
+}
+
+export function saveLearnerState(domainId, state) {
+ localStorage.setItem(KEY_PREFIX + domainId, JSON.stringify(state));
+}
+
+export function resetLearnerState(domainId) {
+ localStorage.removeItem(KEY_PREFIX + domainId);
+}
diff --git a/webui/src/styles.css b/webui/src/styles.css
index 9d15480..893f322 100644
--- a/webui/src/styles.css
+++ b/webui/src/styles.css
@@ -7,108 +7,30 @@
--accent: #2d6cdf;
}
* { box-sizing: border-box; }
-body {
- margin: 0;
- font-family: Arial, Helvetica, sans-serif;
- background: var(--bg);
- color: var(--text);
-}
-.page {
- max-width: 1500px;
- margin: 0 auto;
- padding: 20px;
-}
-.hero {
- background: var(--card);
- border: 1px solid var(--border);
- border-radius: 20px;
- padding: 20px;
- display: flex;
- justify-content: space-between;
- gap: 20px;
- align-items: flex-start;
-}
+body { margin: 0; font-family: Arial, Helvetica, sans-serif; background: var(--bg); color: var(--text); }
+.page { max-width: 1500px; margin: 0 auto; padding: 20px; }
+.hero { background: var(--card); border: 1px solid var(--border); border-radius: 20px; padding: 20px; display: flex; justify-content: space-between; gap: 20px; align-items: flex-start; }
.hero h1 { margin-top: 0; }
-.hero-actions {
- display: flex;
- gap: 10px;
- flex-wrap: wrap;
-}
-button {
- border: 1px solid var(--border);
- background: white;
- border-radius: 12px;
- padding: 10px 14px;
- cursor: pointer;
-}
+.hero-actions { display: flex; gap: 10px; flex-wrap: wrap; }
+button { border: 1px solid var(--border); background: white; border-radius: 12px; padding: 10px 14px; cursor: pointer; }
button:hover { border-color: var(--accent); }
-.summary-grid {
- margin-top: 16px;
- display: grid;
- grid-template-columns: repeat(4, 1fr);
- gap: 16px;
-}
-.layout {
- margin-top: 16px;
- display: grid;
- grid-template-columns: 290px 1fr 360px;
- gap: 16px;
-}
-.card {
- background: var(--card);
- border: 1px solid var(--border);
- border-radius: 18px;
- padding: 16px;
-}
-.sidebar, .content, .rightbar {
- display: flex;
- flex-direction: column;
- gap: 16px;
-}
-.concept-btn {
- width: 100%;
- text-align: left;
- display: flex;
- justify-content: space-between;
- gap: 8px;
- margin-bottom: 10px;
-}
-.concept-btn.active {
- border-color: var(--accent);
- box-shadow: 0 0 0 2px rgba(45,108,223,0.08);
-}
-.status-pill {
- font-size: 12px;
- padding: 4px 8px;
- border-radius: 999px;
- border: 1px solid var(--border);
- white-space: nowrap;
-}
+.summary-grid { margin-top: 16px; display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }
+.layout { margin-top: 16px; display: grid; grid-template-columns: 290px 1fr 360px; gap: 16px; }
+.card { background: var(--card); border: 1px solid var(--border); border-radius: 18px; padding: 16px; }
+.sidebar, .content, .rightbar { display: flex; flex-direction: column; gap: 16px; }
+.concept-btn { width: 100%; text-align: left; display: flex; justify-content: space-between; gap: 8px; margin-bottom: 10px; }
+.concept-btn.active { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(45,108,223,0.08); }
+.status-pill { font-size: 12px; padding: 4px 8px; border-radius: 999px; border: 1px solid var(--border); white-space: nowrap; }
.status-trusted { background: #e7f7ec; }
.status-provisional { background: #fff6df; }
.status-rejected { background: #fde9e9; }
.status-needs_review { background: #eef2f7; }
-label {
- display: block;
- font-weight: 600;
- margin-bottom: 12px;
-}
-input, textarea, select {
- width: 100%;
- margin-top: 6px;
- border: 1px solid var(--border);
- border-radius: 10px;
- padding: 10px;
- font: inherit;
- background: white;
-}
+label { display: block; font-weight: 600; margin-bottom: 12px; }
+input, textarea, select { width: 100%; margin-top: 6px; border: 1px solid var(--border); border-radius: 10px; padding: 10px; font: inherit; background: white; }
.small { color: var(--muted); }
.big { font-size: 34px; font-weight: 700; }
-.conflict {
- border-top: 1px solid var(--border);
- padding-top: 12px;
- margin-top: 12px;
-}
+.conflict { border-top: 1px solid var(--border); padding-top: 12px; margin-top: 12px; }
+ul { padding-left: 18px; }
@media (max-width: 1100px) {
.summary-grid { grid-template-columns: repeat(2, 1fr); }
.layout { grid-template-columns: 1fr; }
diff --git a/workspace_registry.json b/workspace_registry.json
new file mode 100644
index 0000000..0529980
--- /dev/null
+++ b/workspace_registry.json
@@ -0,0 +1,24 @@
+{
+ "workspaces": [
+ {
+ "workspace_id": "bayes-intro",
+ "title": "Introductory Bayesian Inference",
+ "path": "workspaces/bayes-intro",
+ "created_at": "2026-03-13T12:00:00+00:00",
+ "last_opened_at": "2026-03-13T12:30:00+00:00",
+ "notes": "Initial imported course pack"
+ },
+ {
+ "workspace_id": "stats-foundations",
+ "title": "Statistics Foundations",
+ "path": "workspaces/stats-foundations",
+ "created_at": "2026-03-13T12:05:00+00:00",
+ "last_opened_at": "2026-03-13T12:20:00+00:00",
+ "notes": "Secondary draft pack"
+ }
+ ],
+ "recent_workspace_ids": [
+ "bayes-intro",
+ "stats-foundations"
+ ]
+}
\ No newline at end of file
diff --git a/workspaces/bayes-intro/draft_pack/concepts.yaml b/workspaces/bayes-intro/draft_pack/concepts.yaml
new file mode 100644
index 0000000..8eedf05
--- /dev/null
+++ b/workspaces/bayes-intro/draft_pack/concepts.yaml
@@ -0,0 +1,14 @@
+concepts:
+ - id: descriptive-statistics
+ title: Descriptive Statistics
+ description: Measures of center and spread.
+ prerequisites: []
+ mastery_signals:
+ - Explain mean, median, and variance.
+ - id: probability-basics
+ title: Probability Basics
+ description: Basic event probability and conditional probability.
+ prerequisites:
+ - descriptive-statistics
+ mastery_signals:
+ - Compute a simple conditional probability.
diff --git a/workspaces/bayes-intro/draft_pack/conflict_report.md b/workspaces/bayes-intro/draft_pack/conflict_report.md
new file mode 100644
index 0000000..6a73437
--- /dev/null
+++ b/workspaces/bayes-intro/draft_pack/conflict_report.md
@@ -0,0 +1,3 @@
+# Conflict Report
+
+- Example conflict for review.
diff --git a/workspaces/bayes-intro/draft_pack/pack.yaml b/workspaces/bayes-intro/draft_pack/pack.yaml
new file mode 100644
index 0000000..a65cb57
--- /dev/null
+++ b/workspaces/bayes-intro/draft_pack/pack.yaml
@@ -0,0 +1,3 @@
+name: bayes-intro
+display_name: Introductory Bayesian Inference
+version: 0.1.0-draft
diff --git a/workspaces/bayes-intro/draft_pack/review_report.md b/workspaces/bayes-intro/draft_pack/review_report.md
new file mode 100644
index 0000000..06ea277
--- /dev/null
+++ b/workspaces/bayes-intro/draft_pack/review_report.md
@@ -0,0 +1,3 @@
+# Review Report
+
+- Example review flag.
diff --git a/workspaces/stats-foundations/draft_pack/concepts.yaml b/workspaces/stats-foundations/draft_pack/concepts.yaml
new file mode 100644
index 0000000..8eedf05
--- /dev/null
+++ b/workspaces/stats-foundations/draft_pack/concepts.yaml
@@ -0,0 +1,14 @@
+concepts:
+ - id: descriptive-statistics
+ title: Descriptive Statistics
+ description: Measures of center and spread.
+ prerequisites: []
+ mastery_signals:
+ - Explain mean, median, and variance.
+ - id: probability-basics
+ title: Probability Basics
+ description: Basic event probability and conditional probability.
+ prerequisites:
+ - descriptive-statistics
+ mastery_signals:
+ - Compute a simple conditional probability.
diff --git a/workspaces/stats-foundations/draft_pack/conflict_report.md b/workspaces/stats-foundations/draft_pack/conflict_report.md
new file mode 100644
index 0000000..6a73437
--- /dev/null
+++ b/workspaces/stats-foundations/draft_pack/conflict_report.md
@@ -0,0 +1,3 @@
+# Conflict Report
+
+- Example conflict for review.
diff --git a/workspaces/stats-foundations/draft_pack/pack.yaml b/workspaces/stats-foundations/draft_pack/pack.yaml
new file mode 100644
index 0000000..7c220a8
--- /dev/null
+++ b/workspaces/stats-foundations/draft_pack/pack.yaml
@@ -0,0 +1,3 @@
+name: stats-foundations
+display_name: Statistics Foundations
+version: 0.1.0-draft
diff --git a/workspaces/stats-foundations/draft_pack/review_report.md b/workspaces/stats-foundations/draft_pack/review_report.md
new file mode 100644
index 0000000..06ea277
--- /dev/null
+++ b/workspaces/stats-foundations/draft_pack/review_report.md
@@ -0,0 +1,3 @@
+# Review Report
+
+- Example review flag.