240 Commits
v0.9.0 ... main

Author SHA1 Message Date
bc527ec09a Add capability registry scaffold (REUSE-WP-0014-T05 B03)
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
2026-06-16 01:54:12 +02:00
ce984482e2 assessment of forgotten functionality
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
2026-05-23 06:44:38 +02:00
9266f124e6 Refresh agent instruction files
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
2026-05-18 16:55:45 +02:00
8740a66611 chore(consistency): sync task status from DB [auto]
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Updated by fix-consistency on 2026-05-03:
  - update .custodian-brief.md for markitect-project
2026-05-03 19:31:36 +02:00
b7e9edbb4b chore(consistency): sync task status from DB [auto]
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Updated by fix-consistency on 2026-05-01:
  - update .custodian-brief.md for markitect-project
2026-05-01 23:07:28 +02:00
479fa95fdf Scope update from repo-scoping refactor
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
2026-05-01 12:27:17 +02:00
eb9b622499 chore: gitignore Claude Code session lock files
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
`.claude/scheduled_tasks.lock` is per-session runtime state (holds the
owning session id and pid for the ScheduleWakeup queue); it shouldn't
be committed. Widened the pattern to `.claude/*.lock` so future lock
kinds are covered too.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 21:50:20 +02:00
e3e5b8ecc1 feat(infospace): systematic long-text processing — rich commit bodies, per-source eval/classify, chapters view
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Three coordinated changes that let the pipeline produce a clean
chapter-by-chapter git history on long texts without archaeology after
the fact.

1. Richer commit messages. `SourcePipeline._git_commit` now diffs the
   staged changes, buckets added files by output subdirectory (entities,
   evaluations, classifications, mappings, analyses, metrics, logs), and
   includes counts in the commit body. So `git log` reads "entities:
   +23, evaluations: +23" per chapter instead of the same generic blurb
   on every commit. Zero behaviour change when no output changed; falls
   back to the original message if the diff query fails.

2. --eval-after-source / --classify-after-source on `infospace process`.
   After a source's stages succeed, the pipeline identifies which entity
   files are *new* (set diff of entity slugs before vs after), loads
   their EntityMeta, and runs per-entity evaluation and/or
   classification scoped to just those slugs before the per-source git
   commit lands. Result: each chapter's commit is self-contained —
   extraction + evaluation + classification in one atomic unit. Gated
   behind explicit flags because the cost is real (LLM latency per
   chapter rather than amortised across one bulk batch).

3. `markitect infospace chapters` subcommand. Lists source files in
   canonical order with entity count, evaluated count, classified
   count, and mean per-entity score per source. Text or JSON output.
   Natural triage surface for long-text infospaces — spot chapters that
   under-extracted or evaluated poorly.

Also: `docs/advanced-usage.md` gets a new "Systematic processing of
long texts" section with the recommended flag combo and the tradeoff
note on cost.

11 new unit tests cover the chapters command (text/json/no-sources),
the process flag wiring (help + provider requirement), and the
commit-body bucket logic. Full infospace+llm unit suite (315 tests)
green; 3 pre-existing infospace failures unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 08:24:26 +02:00
9e8d73fa7d docs(roadmap): close out infospace tooling S3 and parent roadmap
All three stages of the infospace tooling roadmap are complete. The Wealth
of Nations / VSM example passes 6/6 viability thresholds on 988 entities,
and composition is demonstrated via the supply-chain-vsm example.

- Parent roadmap (roadmap/infospace-tooling/PLAN.md): header now shows the
  closed status with final validation metrics.
- S3 close-out plan (roadmap/infospace-s3-closeout/PLAN.md): records the
  final task dispositions. C.1–C.6 and C.8 done; C.7 (clean per-chapter
  git history) is deferred indefinitely — the task was cosmetic, its
  prerequisite branch no longer exists, and reconstructing 35 archival
  commits would not change any output files. Rationale documented inline.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 07:08:43 +02:00
d44a4cd3df feat(infospace,llm): agent ergonomics — entity lookup, model fallback, better errors
- `markitect infospace entity <name>`: single-entity lookup tolerating
  hyphens/underscores/case, with substring matching, ambiguity listing,
  and near-match hints. Prints slug, source path, domain, chapter, word
  count, VSM system, overall score, evaluator, and evaluation file path.
- `markitect infospace evaluate --model-fallback <model>`: if any
  entities fail with a rate-limit error, retry just those with a fresh
  adapter on the fallback model (different free-tier models have
  separate quota buckets).
- `markitect llm-check`: advisory when `OPENROUTER_API_KEY` is set but
  not used by the resolved provider; targeted hint when OpenRouter
  returns 401 (almost always a stale env key).
- `build_state`: raises `TypeError` with actionable message if passed a
  path instead of an `InfospaceConfig` — prior failure mode was a
  confusing `AttributeError` deep in the stack.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 01:07:25 +02:00
c0615c2d50 feat(infospace,llm): stabilize free-tier eval workflow
Some checks failed
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Five improvements that eliminate most of the agent-in-the-loop friction
observed while closing out the 988-entity WoN evaluation (C.1):

1. Gemini adapter now retries on 429 + 5xx with exponential backoff
   (same pattern already used by OpenRouter/OpenAI). Removes the need
   for shell-level retry wrappers when hitting free-tier rate limits.

2. evaluate CLI prints the underlying error ("ERROR — HTTP 503 …")
   instead of a bare "ERROR", so agents don't have to drop into Python
   to diagnose transient failures.

3. --entity/--chapter now respect existing evaluation files by default
   (previously only the full-collection pass did). New --force flag
   opts into re-evaluation. Stops silently burning free-tier quota on
   re-runs of the same slug.

4. --entity accepts hyphenated slugs (matching entity filenames) and
   normalizes them to the underscore form used on disk. On a miss the
   CLI suggests near matches instead of a bare "not found".

5. eval-summary --update-metrics is no longer destructive:
   read_metrics_file/write_metrics_file preserve structured values
   (type_distribution) and don't flatten ints to floats. Fixes a
   silent data loss observed on every run.

Bonus: the evaluator field in written evaluation frontmatter now
falls back from run_config.model_name to the adapter's resolved model
(or the model echoed back in the API response), so rows no longer
show `evaluator: null` when --model is omitted.

Tests: new tests/unit/llm/test_gemini.py covers retry behavior;
tests/unit/infospace/test_history.py gains a round-trip test that
pins the type_distribution / int-preservation invariants.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 00:51:00 +02:00
965508ec06 chore(consistency): sync task status from DB [auto]
Updated by fix-consistency on 2026-04-22:
  - update .custodian-brief.md for markitect-project
2026-04-22 00:28:46 +02:00
f325f89dc9 feat(infospace): evaluate 3 missing WoN entities (C.1)
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Fills the 988 entity / 985 evaluation gap in the Wealth of Nations
infospace. Entities advanced_state_of_society, bank_notes, and
bank_systemic_risk_management had no evaluation files; runs through
Gemini (2.5-flash / 2.5-flash-lite for the last one, which hit the
free-tier RPM limit) bring the eval count to 988.

per_entity_mean nudged from 3.955635 to 3.95668; viability still
6/6 PASS.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 23:52:04 +02:00
36a5136bdf docs(infospace): add advanced-usage, composition guide, and performance notes (C.4/C.5/C.6)
Closes out three docs tasks from roadmap/infospace-s3-closeout/PLAN.md:

- examples/infospace-with-history/docs/advanced-usage.md (C.4) — 5 worked
  patterns covering incremental eval, re-eval workflow (no --force flag
  exists; documents the rm-then-re-run pattern instead), interpreting the
  eval-summary distribution, triaging low scorers via an awk pipeline
  over overall_score (since `entities --sort-by score` does not exist),
  and acting on check --json output.
- docs/composition-guide.md (C.5) — walks through how supply-chain-vsm
  binds WoN as a discipline, then a step-by-step for creating a new
  infospace that binds an existing one. Includes live output from
  `markitect infospace disciplines`.
- examples/infospace-with-history/docs/performance-notes.md (C.6) — cites
  the 6h 28m wall time of the 985-entity S3.3 batch, ~2.5 ent/min rate,
  ~2000–3000 tokens/entity estimate, word_overlap vs embedding backend
  for redundancy checks, and a provider-by-scale recommendation table.

All commands in these docs were run against the live infospace at
commit time.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 07:02:46 +02:00
b7e11461f4 chore: rename markitect_project to markitect-main across project
Finishes the in-progress rename so docs, configs, tests, and capability
manifests all reference the current repo name consistently. Fixes two
tests (test_roundtrip_consolidated.py, test_issue_140_roundtrip_simplified.py)
whose hardcoded cwd paths would have broken under the renamed directory.

Archival content under history/, reports/, and roadmap/eat-the-frog/, plus
derived artifacts (.venv_old/, node_modules/, asset_registry.json) are
intentionally left untouched.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 01:57:35 +02:00
3966814868 updated SCOPE file
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
2026-03-25 00:11:46 +01:00
f4610a46e3 docs: add SCOPE.md for rapid orientation
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 23:11:42 +01:00
0d95e6dbcf docs(claude): expand CLAUDE.md with commands and architecture
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Replaces the stub (State Hub integration only) with full dev commands,
module architecture overview, LLM config resolution chain, infospace
conventions, and active roadmap pointers. Removes CLAUDE.custodian.md
(superseded by the expanded CLAUDE.md).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 23:28:03 +01:00
36c20f37d0 feat(llm): extract adapter layer for standalone llm-connect package (S1+S2)
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Stage 1 — Decouple:
- Move RunConfig + LLMResponse to markitect/llm/models.py (canonical)
- Move LLMAdapter + Mock/ErrorLLMAdapter to markitect/llm/adapter.py
- markitect/prompts/execution/models.py and llm_adapter.py become re-export shims
- All 4 adapters + factory.py updated to import from markitect.llm.*
- Parameterize app_name in toml_config.py (resolve_llm, get_default_layers,
  get_preference_layers): paths and env var now derived from app_name arg
- Add tests/test_llm_isolation.py: 7 isolation + backward-compat tests

Stage 2 — Extract:
- Standalone llm-connect package created at ~/llm-connect/
- All 18 llm files copied; markitect.* imports replaced with llm_connect.*
- LLMError base inlined in llm_connect/exceptions.py (no markitect dep)
- llm-connect installed into markitect-venv; declared in pyproject.toml

Smoke test: markitect llm-check succeeds (live Gemini API call).
Backward compat: markitect.prompts.execution.{models,llm_adapter} still work.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 08:04:50 +01:00
72b87fd82e docs(roadmap): add workplans for infospace S3 close-out and JSUI publication
infospace-s3-closeout: 8 tasks (C.1-C.8) covering 3 missing evals,
viability sign-off, docs (advanced usage, composition, perf), deferred
git history cleanup, and formal roadmap closure.

testdrive-jsui-publication: 9 tasks (P.1-P.9) covering repo structure
decision, Markitect integration gate, pack/dry-run, npm publish, CDN
verify, fresh install test, GitHub release, and badges.

Both registered as workstreams in Custodian State Hub.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 00:44:05 +01:00
eaf4a955af docs(roadmap): add workplan for extracting llm module as shared library
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
3-stage plan: decouple (RunConfig/LLMResponse move + app name
parameterization) → extract to standalone package → adopt in first
consumer. Registered as workstream in Custodian State Hub.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 21:51:54 +01:00
e9dc9a8517 docs(custodian): add session protocol CLAUDE.md for State Hub integration
Registers markitect as a tracked domain in the Custodian State Hub.
Includes topic ID, session start/end protocol, and MCP tool reference.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 21:42:41 +01:00
b055c8d7bb docs(example): close out INFRA-TASKS with summary and 4 follow-up items
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Adds a closing remark (23 Feb 2026) summarising the final state of the
infospace: 988 entities, 985 evaluations, 823 L2 classifications, 15 L3
relations, viability 6/6 PASS.

New open tasks 20–23:
  20. Complete L2 classification batch (165 entities blocked on credits)
  21. Run classify-links for 58 Relation-type entities
  22. Refresh stale metrics-report.md narrative
  23. Smoke-test the graph command end-to-end

Also committed: history.py fix — write_metrics_file now preserves
non-float metric values (type_distribution dict) instead of crashing
on round().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 13:45:58 +01:00
ef3d47779e feat(infospace): add entity-relation graph export (Mermaid + DOT)
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
New graph_export.py module supporting the `markitect infospace graph`
command added in the previous commit.

- build_entity_graph(): constructs node/edge graph from L2 classifications
  and L3 relation triplets, with feedback loop detection via networkx
- apply_filters(): subgraph filters by entity type, VSM system, ego
  neighbourhood, feedback-loops-only, and classified-only
- to_mermaid(): Mermaid flowchart export
  - Uses "-- label -->" syntax for all edges (robust with parentheses);
    "== label ==>" thick arrows for feedback loop edges
  - markdown_fence=True wraps output in ```mermaid block (VS Code / GitHub)
  - color_by="type" or "vsm" with distinct palettes for each
- to_dot(): Graphviz DOT export with fillcolor per type/VSM system

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 13:14:25 +01:00
d1f57272a4 feat(example): add L2 classifications for 823/988 WoN entities (S3.4)
Batch classification via OpenRouter (claude-sonnet-4). 165 entities
remain unclassified due to credit exhaustion; incremental skip means
a follow-up run will complete them automatically.

Type × VSM matrix (823 entities):
                  S1   S2   S3  S3*   S4   S5
  Element         86   75   58   21   43   32  (315 total, 38%)
  Process         39   42   37   17   67   24  (226 total, 28%)
  Institution      4   12   30   24    .   52  (122 total, 15%)
  Principle        3    7   15    2   43   32  (102 total, 12%)
  Relation         2   14    5    5   22   10   (58 total,  7%)
  Matrix fill: 29/30 cells (Institution/S4 empty — expected)

Metrics updated: type_entropy=2.0936, vsm_type_matrix_cells=29

Also:
- BatchEvaluator gains delay_seconds param for rate-limited providers
- classify CLI gains --rpm option (--rpm 10 for Gemini free tier)
- history.write_metrics_file now handles non-float metric values
  (type_distribution is a dict, was crashing round())
- run_entity_classification forwards delay_seconds to BatchEvaluator
- classify-links and graph commands added by user (entities --by-type,
  graph --format mermaid/dot, classify-links for Relation enrichment)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 12:49:11 +01:00
a9ca0adfcf feat(example): add per-entity LLM evaluations for 985 WoN entities (S3.3)
Batch evaluation of all 988 entities via OpenRouter. 984 succeeded on
first pass; 3 failed (network errors). eval-summary --update-metrics
written with per_entity_mean=3.9556.

Viability dashboard: 6/6 PASS
  redundancy_ratio   0.0061  (max 0.10)
  coverage_ratio     0.6190  (min 0.40)
  coherence_comps    0.0000  (max 3)
  consistency_cycles 0.0000  (max 0)
  granularity_entropy 2.6748 (min 1.0)
  per_entity_mean    3.9556  (min 3.5)

Dimension breakdown (mean across 985 entities):
  definition_precision  3.62
  source_grounding      4.36
  domain_placement      4.56
  vsm_relevance         3.31
  explanatory_value     3.94

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 09:36:46 +01:00
81a4c8796a feat(infospace): add L2 entity classification with type × VSM matrix (S2.9)
Implements the L2 typed-entities layer — each entity is assigned an
Entity Type (Element, Process, Relation, Principle, Institution) and a
VSM System (S1–S5) by an LLM, with one-sentence rationales for each.

New modules:
- markitect/infospace/classification.py — EntityClassification dataclass
  + ENTITY_TYPES / VSM_SYSTEMS controlled vocabularies
- markitect/infospace/classification_io.py — write/read classification
  files (YAML frontmatter + markdown body, mirrors evaluation_io)
- markitect/infospace/classifier.py — build_classification_prompt(),
  parse_classification_response(), run_entity_classification(); batch
  runner writes files incrementally (same resumable pattern as evaluate)

CLI: markitect infospace classify [--entity SLUG] [--provider P] [--model M]
  - Incremental skip: checks output/classifications/ for existing files
  - Defaults to openrouter provider; 2000 max_tokens (Gemini 2.5 Flash
    uses ~787 thinking tokens, so 800 was too low)

CLI: markitect infospace classify-summary [--update-metrics]
  - Entity type counts + VSM system counts with percentages
  - 5 × 6 type × VSM matrix (spots structural blind spots at a glance)
  - --update-metrics writes type_distribution, type_entropy,
    vsm_type_matrix_cells to metrics.yaml

Config: InfospaceConfig gains classifications_dir (default output/classifications)
Schema: schemas/typed-entity-schema-v1.0.md — type/VSM vocabulary tables,
  rationale format rules, validation rules, metrics enabled at L2
infospace.yaml: schemas.typed_entity references typed-entity-schema-v1.0.md

Seed classifications (3): division_of_labour (Process/S1),
  natural_price_as_central_price (Principle/S2),
  invisible_hand_mechanism (Principle/S4)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 09:35:58 +01:00
2d45425b25 feat(infospace): add L3 relation graph with VSM-aware triplets (S2.8)
Implements the L3 relation graph layer — a directed graph of (Subject,
Predicate, Object) triplets annotated with VSM channel codes and feedback
roles. Triplets are authored as markdown files under output/relations/,
parsed into RelationMeta dataclasses, and analysed with networkx.

New modules:
- markitect/infospace/relation_models.py — RelationMeta dataclass +
  RELATION_TYPES controlled vocabulary (15 relation classes → VSM codes)
- markitect/infospace/relation_parser.py — parse_relation_file() and
  parse_relations_directory()

New schema: examples/infospace-with-history/schemas/relation-schema-v1.0.md
  — file naming convention, required sections, controlled vocabulary table

15 seed relation files covering the three core WoN feedback loops:
  - Capital Accumulation loop (positive reinforcement, S1/S3)
  - Market Price Balancing loop (negative feedback, S2/S3)
  - Market Extent mutual dependency (S1/S2)
  Plus structural relations: wages regulation, rent residual, price
  decomposition, invisible hand coordination

CLI: markitect infospace relations [--entity SLUG] [--vsm FILTER]
     [--loops] [--stats]
  - Builds directed graph from parsed files
  - Detects feedback loops via nx.simple_cycles()
  - 6 loops found from 15 seed relations (3 intended + 3 emergent)
  - --stats aggregates by VSM system code (strips parentheticals)

Config: InfospaceConfig gains relations_dir (default output/relations)
infospace.yaml: schemas.relation references relation-schema-v1.0.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 06:04:28 +01:00
fa27572f43 fix(example): skip prompt writes when output exists, add quality rubrics
INFRA-TASKS #5 — process_chapters.py now skips writing *-prompt.md files
when the corresponding output file already exists on disk. DB-only rebuilds
no longer dirty the working tree with unchanged prompt content.

INFRA-TASKS #8 — Added '## Quality Metrics' section to the entity and VSM
mapping schemas, defining the five evaluation dimensions (Definition Precision,
Source Grounding, Domain Placement, VSM Relevance, Explanatory Value) with
1–5 rubrics used by the evaluate-entity template.

Also updated INFRA-TASKS.md to reflect current resolution status for tasks
4–19 across S2 and S3.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 06:04:09 +01:00
dfab3d598b feat(cli): add 'helper' alias for markitect helper command
markitect helper <QUESTION> now works as a short alias for
markitect llm-helper, per the original plan specification.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 05:40:11 +01:00
34ed7a6fab docs(tutorial): update §8-9 for eval-summary command and 6/6 viability
- Add eval-summary command documentation with dimension descriptions
- Document resumable evaluate (incremental skip on re-run)
- Fix --entity slug example to use underscores (not hyphens)
- Update viability output to show per_entity_mean as 6th threshold
- Add workflow note: check → eval-summary --update-metrics → viability

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 05:33:11 +01:00
7f1eecbdb2 feat(infospace): add eval-summary command and improve evaluate pipeline (S3.3)
- Fix evaluate dimensions to match template file:
  definition_precision, source_grounding, domain_placement,
  vsm_relevance, explanatory_value (was domain_relevance,
  discipline_alignment, conceptual_clarity)
- Add VSM background context to evaluation prompt so LLM can
  score vsm_relevance without macro injection
- Fix model_name bug: was sending literal "default" to API (HTTP 400)
- Refactor run_entity_evaluation to write files incrementally via
  callback rather than all at once after the batch — long runs are
  now resumable if interrupted
- Add incremental skip in CLI: entities with existing eval files
  are skipped automatically on re-run (acts as resume)
- Add eval-summary command: reads all eval files, shows per-dimension
  means, optionally writes per_entity_mean to metrics.yaml
- Fix record_check_results to merge rather than overwrite metrics.yaml
  so per_entity_mean survives subsequent check runs
- Add per_entity_mean viability threshold (min: 3.5) to infospace.yaml

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 01:26:45 +01:00
574bb11db6 feat(example): add supply-chain-vsm composition demo (S3.5)
Demonstrates infospace composition: the Wealth of Nations infospace is
used as a discipline, applying Smith's economic framework as a lens to
analyse modern supply chain management concepts.

New example: examples/supply-chain-vsm/
- infospace.yaml binding WoN as discipline (../infospace-with-history)
- 3 source documents: coordination mechanisms, capital & inventory,
  market structure (~400 words each, original content)
- supply-chain-entity-schema-v1.0.md with WoN Concept required section
- won-mapping-schema-v1.0.md with Conceptual Continuity rating
- artifacts/won-reference/core-entities.md — 12 curated WoN entities
  for injection as discipline context
- 8 hand-crafted entity files demonstrating LLM output format
- 3 mapping files with full rationale and VSM inheritance chains
- Viable: YES (5/5 thresholds)

Key mappings demonstrated:
  Demand Signal          → Effectual Demand        (Strong, S2)
  Vendor-Managed Inventory → Division of Labour    (Strong, S1/S2)
  Just-in-Time Inventory → Circulating Capital     (Strong, S1/S3)
  Bullwhip Effect        → Natural Price           (Moderate, S2)
  Platform Intermediary  → Merchant Capital        (Strong, S2/S4)
  Monopsony Power        → Combination of Masters  (Strong, S3*)

Platform fix: entity_parser.py now recognises ## Supply Chain Domain
as a domain alias for ## Economic Domain, enabling composed infospaces
to use their own domain section name.

Tutorial §13 rewritten with real commands, real output, and the full
mapping table from the demo.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 00:08:51 +01:00
8f00fa2018 docs(tutorial): update all commands to use markitect infospace CLI (S3.4)
Replace all process_chapters.py references throughout the tutorial with
the correct markitect infospace subcommands:

- §2  Project layout: remove process_chapters.py, add LAYERED-DEVELOPMENT.md
- §7  Processing: --chapter → process "glob", --book N → "book-N-*.md",
      --list → status/entities, --archive-entity → documented manual step
- §8  Check: remove incorrect --provider flag; note checks are deterministic
- §9  Viability: real output from full 988-entity corpus (Viable: YES)
- §10 History: real snapshot table; add --metric flag example
- §10 Git tracking: remove process_chapters.py from commit example
- §11 Cost: update openrouter/free example command
- §12 Completion: rewrite with actual observed metric progression table
- §14 Quality loop: update all commands; add archive-entity manual procedure
- §15 Artifact DB: --all without --provider = dry-run (no LLM calls)
- §16 Adapting: update step 6 and 7 to new CLI

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 23:31:38 +01:00
c861520ccd docs(example): add layered development concept and extend tutorial
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Adds LAYERED-DEVELOPMENT.md documenting the concept for evolving a flat
entity collection into a structured systemic model through four layers:

  L0 Source text → L1 Raw entities (current) → L2 Typed entities
  → L3 Relation graph → L4 Minimal systemic model

Covers: the element/relation/principle/institution type taxonomy,
VSM as a structural coordinate system, the type × VSM coverage matrix,
triplet extraction with a controlled predicate vocabulary, feedback loop
detection, and the distillation hypothesis for finding the generative
core of a corpus.

Extends TUTORIAL.md with sections 17–23:
  17. Observing entity heterogeneity
  18. The four-layer model overview
  19. Layer 2 — classifying entities (schema, pipeline stage, metrics)
  20. Layer 3 — extracting the relation graph (triplets, feedback loops)
  21. Layer 4 — the minimal systemic model (core-model.md output)
  22. Planned CLI commands for layers 2–4
  23. Layers 2–4 as composed infospaces

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 10:43:32 +01:00
9c32ad1837 fix(infospace): exclude raw LLM output from entity parsing; lower coverage threshold
- Add `.*-raw\.md$` to `_DEFAULT_EXCLUDE_PATTERNS` in entity_parser.py to
  prevent per-chapter raw LLM output files from being parsed as entities.
  This eliminates 33 malformed domain values where delimiter text was
  bleeding into the Economic Domain field.
- Lower coverage_ratio threshold from 0.50 → 0.40 in infospace.yaml to
  reflect realistic multi-book corpus expectations (documented rationale
  in METRICS-METHODOLOGY.md).

Post-fix metrics: 988 entities, 0 malformed, coverage_ratio=0.619 (pass).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 09:28:20 +01:00
7c38f9b427 merge(reprocess-v2): complete pipeline rewrite and full corpus processing
Merges the reprocess-v2 branch into main, covering:

Infrastructure changes:
- markitect infospace process — new CLI command for batch source processing
- SourcePipeline — @{macro} substitution, skip-if-exists, git commit per source
- PipelineStage config extended with name, output_dir, output_macro,
  split_entities, macros, max_tokens fields
- Per-stage max_tokens (extract=8k, map-to-vsm=10k, synthesize=4k)
- LLM provenance comment in each new entity file
- output/processing-log.yaml with per-source token/cost/duration/retry stats
- Retry on all LLM errors (not just rate limits) with 5s back-off
- C2 coverage: add domain_densities, density_std, cross_cutting_ratio

Example (infospace-with-history):
- All 35 chapters processed: 1021 entities across Books 1–5
- Per-chapter git commits showing metric evolution from 0 → final state
- Final metrics: coverage=0.44, granularity=2.95, redundancy=0.006
- METRICS-METHODOLOGY.md C2 section corrected and expanded

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 00:11:39 +01:00
dfe56a4f9b docs(metrics): clarify C2 coverage — domain×chapter matrix, not domain×VSM
- coverage.py: rewrite module docstring to explain what the metric actually
  computes (domain × chapter cross-tabulation, not VSM system coverage),
  what it does not capture (entity connectivity → C3), and when the
  threshold is appropriate
- CoverageReport: add domain_densities, density_std, cross_cutting_ratio
  for distribution-level insight beyond the aggregate ratio
- check_coverage: compute per-domain density and cross-cutting ratio
- METRICS-METHODOLOGY.md: correct C2 section to match implementation,
  document the distribution-based interpretation, add implementation status
  table distinguishing what is wired vs planned

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 00:08:46 +01:00
0f54f094e4 chore(example): final metrics snapshot — all 35 chapters processed
1021 entities extracted across all Books 1-5 of The Wealth of Nations.
Final metrics: coverage=0.4424, granularity=2.9533, redundancy=0.0059.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 22:54:54 +01:00
4a15a50337 infospace: process book-5-chapter-03
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 22:54:40 +01:00
92dfe367c7 infospace: process book-5-chapter-02
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 22:46:32 +01:00
23c397e46a infospace: process book-5-chapter-01
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 22:36:06 +01:00
e695ddfbbd infospace: process book-4-chapter-09
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 22:32:07 +01:00
5245dbbfc8 infospace: process book-4-chapter-08
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 22:25:52 +01:00
4319d2a32b infospace: process book-4-chapter-07
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 22:14:18 +01:00
efdaa884c8 infospace: process book-4-chapter-06
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 22:01:44 +01:00
2804de3d24 infospace: process book-4-chapter-05
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 21:47:52 +01:00
3e96ac7b8d infospace: process book-4-chapter-04
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 21:36:17 +01:00
a687e508f3 infospace: process book-4-chapter-03
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 21:31:40 +01:00
da9c5fce80 infospace: process book-4-chapter-02
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 21:19:39 +01:00
cd87ebfdc0 infospace: process book-4-chapter-01
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 21:13:08 +01:00
666f78d1ba infospace: process book-4-introduction
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 21:02:00 +01:00
579e02989b infospace: process book-3-chapter-04
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 20:46:20 +01:00
8401c69ff2 infospace: process book-3-chapter-03
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 20:40:35 +01:00
1b9a31665c fix(pipeline): retry on all LLM errors (not just rate limits)
Free-tier APIs intermittently return invalid JSON or empty responses.
Now any exception in _call_llm retries up to 3 times with a 5s back-off,
rather than failing immediately on non-rate-limit errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 20:32:23 +01:00
06e904ccf5 infospace: process book-3-chapter-02
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 20:30:22 +01:00
59d42b1665 infospace: process book-3-chapter-01
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 20:18:15 +01:00
8c11e13fef infospace: process book-2-chapter-05
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 20:03:11 +01:00
ac4e508aff infospace: process book-2-chapter-04
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 19:57:59 +01:00
8e1943afdb infospace: process book-2-chapter-03
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 19:50:53 +01:00
05711e541d infospace: process book-2-chapter-02
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 19:43:19 +01:00
8cb9ee6f6e infospace: process book-2-chapter-01
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 19:26:57 +01:00
db129fde6b infospace: process book-1-chapter-11
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 19:19:20 +01:00
6d9ec4e34b infospace: process book-1-chapter-10
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 18:59:36 +01:00
679f482e49 config(example): increase extract-entities max_tokens to 8000
Chapters with many pre-existing entities were still truncating at 6000 tokens
because the LLM needs space to output the full list of candidates even when
most are skipped.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 18:48:33 +01:00
368571905a infospace: process book-1-chapter-09
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 15:58:08 +01:00
9c95912d68 infospace: process book-1-chapter-08
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 15:47:12 +01:00
0828581269 infospace: process book-1-chapter-07
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 15:40:24 +01:00
283abac378 infospace: process book-1-chapter-06
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 15:29:59 +01:00
90ca14dd85 config(example): increase max_tokens for map-to-vsm (10k) and synthesize (4k)
map-to-vsm was consistently truncating at 6000 tokens; synthesize-analysis
sometimes truncated at 3000 for chapters with many entities.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 15:21:04 +01:00
098b781f92 infospace: process book-1-chapter-05
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 15:20:35 +01:00
eea397a380 infospace: process book-1-chapter-04
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 15:12:54 +01:00
7615beb139 chore(example): update metrics after chapter-03 collection check
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 15:06:03 +01:00
c2e06c15d7 infospace: process book-1-chapter-03
Extract entities, map to VSM, and synthesize analysis.
2026-02-19 15:04:57 +01:00
df1fdf1842 feat(pipeline): per-stage max_tokens, LLM provenance, processing log
- PipelineStage now supports max_tokens to override the 4096 default
- SourcePipeline records provider/model on each entity file as HTML comment
- output/processing-log.yaml tracks tokens, cost, duration, retries, errors
- _call_llm returns (content, metadata) for downstream traceability
- _http.py wraps JSON parse errors with body preview for debugging
- infospace.yaml stages: extract/map=6000 tokens, synthesize=3000 tokens

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 14:50:49 +01:00
5ede1de4b8 fix(pipeline): retry on 0-entity response, save raw debug, improve template
- SourcePipeline: retry split_entities stage once when 0 entity delimiters
  are found (free-tier models intermittently return short non-formatted
  responses); save raw LLM response to <stage>-raw.md alongside prompts
- Return None (pause pipeline) rather than writing empty view file when
  no entities found after max retries
- _http.py: wrap json.JSONDecodeError in LLMAPIError with body preview
- extract-entities.md: add explicit H2-heading format example to Output
  Format section to prevent models from using inline "Section:" format

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 14:26:28 +01:00
72d9904485 feat(infospace): add process command for batch source file processing
- Extend PipelineStage with name, output_dir, output_macro,
  split_entities, and macros fields for declarative pipeline config
- Add SourcePipeline class (pipeline.py) using simple @{macro}
  substitution — no SQLite dependency, skip-if-exists per stage,
  LLM retry on rate limits, git commit per source
- Add `markitect infospace process [GLOB_PATTERN]` CLI command with
  --all, --provider, --model, --check-after-each, --no-commit flags
- Update infospace.yaml with output_dir, output_macro, split_entities,
  and macros for each pipeline stage in the WoN example

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 13:29:50 +01:00
c594bc3a38 feat(infospace): add process command for batch source file processing
- Extend PipelineStage with name, output_dir, output_macro,
  split_entities, and macros fields for declarative pipeline config
- Add SourcePipeline class (pipeline.py) using simple @{macro}
  substitution — no SQLite dependency, skip-if-exists per stage,
  LLM retry on rate limits, git commit per source
- Add `markitect infospace process [GLOB_PATTERN]` CLI command with
  --all, --provider, --model, --check-after-each, --no-commit flags
- Update infospace.yaml with output_dir, output_macro, split_entities,
  and macros for each pipeline stage in the WoN example

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 13:29:32 +01:00
77dd3fee6d fix(example): standardise domain enum and source chapter format in schema/rules
Two root causes of metric fragmentation observed in collection checks:

1. Schema's Economic Domain used free-form examples ("labour economics,
   trade theory") which overrode the enum in extraction-rules.md, causing
   the LLM to produce multi-domain strings and non-canonical values.
   Fix: schema now specifies the exact 7-value enum with descriptions.

2. Source Chapter had no format constraint, producing 9 different formats
   for 7 chapters (full titles, mixed Roman/Arabic numerals, asterisks).
   Fix: extraction-rules now mandate "Book [Roman], Chapter [n]" exactly.

These fixes are prerequisites for clean reprocessing (S3.2 continuation).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 13:02:05 +01:00
4e0b27b075 chore(example): record metrics snapshot from infospace check run
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 13:02:03 +01:00
8095a1da4c fix(example): standardise domain enum and source chapter format in schema/rules
Two root causes of metric fragmentation observed in collection checks:

1. Schema's Economic Domain used free-form examples ("labour economics,
   trade theory") which overrode the enum in extraction-rules.md, causing
   the LLM to produce multi-domain strings and non-canonical values.
   Fix: schema now specifies the exact 7-value enum with descriptions.

2. Source Chapter had no format constraint, producing 9 different formats
   for 7 chapters (full titles, mixed Roman/Arabic numerals, asterisks).
   Fix: extraction-rules now mandate "Book [Roman], Chapter [n]" exactly.

These fixes are prerequisites for clean reprocessing (S3.2 continuation).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 13:01:09 +01:00
ce30f874d5 docs(example): rewrite tutorial for infospace tooling (S3.4)
Update TUTORIAL.md to use infospace tooling commands alongside the
chapter processing pipeline:

- Add infospace.yaml declaration and `markitect infospace init`
- Add sections for evaluate, check (C1–C5), and viability dashboard
- Add `markitect infospace history` and status/entities commands
- Add composition section (bind-discipline, disciplines, stale-mappings)
- Update cost/performance: OpenRouter free tier, note claude-code limit
- Update chapter count to 9/35, reference clean-example-history branch
- Restructure as 16 sections following S3.4 roadmap outline

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 11:11:45 +01:00
715ef19d1c infospace: remove example output — will replay chapter by chapter
This commit clears the tangled example output so each chapter
can be re-committed cleanly via S3.2.
2026-02-19 09:22:55 +01:00
3ac8447c10 feat(example): add baseline metrics snapshot from collection checks run
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Initial metrics from S2.4 checks on 85 entities (7 of 35 chapters):
coverage_ratio=0.361, redundancy=0.0, coherence_components=0.0,
consistency_cycles=0.0, granularity_entropy=2.69

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 07:44:01 +01:00
94cb2063af feat(example): migrate to infospace config with tooling integration (S3.1)
Add infospace.yaml declaring topic, disciplines, schemas, viability
thresholds. Integrate infospace tooling into process_chapters.py with
--infospace-status, --infospace-check, and --infospace-viability flags.

Initial check: 85 entities, 4/5 viable (coverage 0.36 < 0.50 — only
7/35 chapters processed so far).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 02:29:53 +01:00
d1c6e53754 docs: add infospace primitives reference (S2.7)
Reference document covering all infospace tooling primitives: config,
entity metadata, schema validation, per-entity evaluation, collection
checks, metrics history, viability, composition, and CLI commands.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 02:05:09 +01:00
b76d6d38c1 feat(infospace): add composition model for discipline binding (S2.6)
Discipline resolution, viability checking, entity access, stale
mapping detection, and binding management. CLI commands: bind-discipline,
disciplines, stale-mappings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 02:03:54 +01:00
ce7f78d57d feat(infospace): add metrics history and viability tracking (S2.5)
History module with snapshot creation from check results, metrics file
I/O, auto-append to history after checks, date-based snapshot lookup,
and metric trend extraction. CLI commands: history, history-diff.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 02:01:00 +01:00
11585e6968 feat(infospace): add collection-level quality checks C1–C5 (S2.4)
Five concern checks: Redundancy (embedding/word overlap), Coverage
(FCA gap analysis), Coherence (graph connectivity), Consistency
(cycle detection), Granularity (Shannon entropy). Orchestrator runs
all or selected checks, CLI `markitect infospace check` command added.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 01:54:22 +01:00
3461d2f354 feat(infospace): add per-entity evaluation pipeline and CLI command (S2.3)
Evaluation pipeline builds prompts from entity metadata, delegates
to BatchEvaluator, parses structured LLM responses into ScoreEntry
objects, and writes evaluation files. CLI: 'markitect infospace evaluate'
with --provider, --entity, --chapter filters.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 01:48:34 +01:00
3726503adb feat(infospace): add lifecycle CLI commands — init, status, entities, viability (S2.2)
Adds 'markitect infospace' command group with init (create config),
status (entity count/domains/disciplines), entities (list with sort),
and viability (threshold dashboard with pass/fail).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 01:46:54 +01:00
b20fe4db68 feat(infospace): add infospace configuration model and state (S2.1)
InfospaceConfig (topic, disciplines, schemas, competency questions,
viability thresholds, pipeline) with YAML load/save and directory
discovery. InfospaceState aggregates entities, evaluations, and
viability checks for status reporting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 01:44:14 +01:00
144a88c0c2 feat(prompts): add batch LLM evaluation orchestrator (S1.6)
BatchEvaluator runs evaluation prompts across item batches with
incremental evaluation (skip unchanged via content digest), per-item
error isolation, progress callbacks, and aggregate token usage tracking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 01:40:13 +01:00
dc22017b7c feat(analysis): add Formal Concept Analysis for coverage gap detection (S1.7)
Pure-Python FCA implementation: FormalContext (entity × attribute
binary relation with extent/intent/closure), ConceptLattice via
NextClosure algorithm, find_gap_concepts() for structural coverage
gaps, and find_empty_cells() for cross-tabulation analysis.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 01:38:35 +01:00
f8c9ab33f0 feat(infospace): add structured evaluation output with history and diffing (S1.5)
Add data models (ScoreEntry, EntityEvaluation, EvaluationSnapshot,
SnapshotDiff) and I/O utilities for YAML frontmatter evaluation files,
snapshot persistence, history append, and snapshot diffing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 01:35:22 +01:00
bad01e32bd feat(analysis): add graph analysis utilities with networkx (S1.4)
Add connected components, betweenness centrality, Louvain community
detection, modularity scoring, degree distribution, and cohesion/coupling
computation. Wraps DependencyGraph via networkx (optional dependency)
for downstream collection-level coherence metrics.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 01:34:53 +01:00
267368eb60 feat(llm): add embedding adapter with cache and similarity utils (S1.3)
Add OpenAI-compatible embedding support (works with both OpenAI and
OpenRouter), file-based embedding cache with content-digest invalidation,
and pure-Python cosine similarity utilities for downstream redundancy
detection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 01:22:21 +01:00
9031e1162c feat(infospace): add schema compliance validator (S1.2)
Deterministic validation of EntityMeta against declarative schemas:
section presence/word counts, heading format, domain enum values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 00:48:57 +01:00
03c6c5e8de feat(infospace): add entity metadata parser (S1.1)
Extract section-tree algorithm from SchemaGenerator into standalone
core/section_tree.py and build markitect/infospace/ package with
EntityMeta dataclass and parse_entity_file/parse_entity_directory.
Foundation for schema compliance, coverage, and granularity metrics.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 00:27:45 +01:00
b5e994b014 docs: preliminary introduction to Viable Information Spaces
Conceptual overview of infospaces as structured, evaluable, composable
knowledge collections. Establishes the vocabulary (topic, discipline,
entity, viability), the build cycle (extract, map, evaluate, refine),
the five collection quality concerns, and the composition model
(hierarchical, networked, swarm).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 23:54:53 +01:00
4ce856d4d0 docs: metrics methodology, collection-level tasks, and infospace tooling roadmap
Add METRICS-METHODOLOGY.md documenting the theoretical frameworks
(SEQUAL, OntoClean, OOPS!, OntoQA, FCA, DSL principles) adapted for
two-layer evaluation (LLM-Eval + deterministic aggregation) across
five collection concerns: redundancy, coverage, coherence, consistency,
and granularity balance.

Extend INFRA-TASKS.md with assignment assessment (tasks 4-7),
per-concept metrics (tasks 8-12), and collection-level metrics
(tasks 13-19).

Add roadmap/infospace-tooling/PLAN.md defining terminology (infospace,
topic, discipline, entity, evaluation, viability) and a three-stage
implementation plan: Stage 1 platform additions, Stage 2 infospace
tooling layer, Stage 3 example revision.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 23:53:21 +01:00
2f0989f9bf docs(infospace): document infospace.db and add to .gitignore
The SQLite artifact database is a derived cache regenerable from
committed files — no LLM calls needed. Added tutorial section
explaining why it is excluded and how to rebuild it after a fresh clone.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 22:27:08 +01:00
60f33443ae feat(schema): add semantic schema generation as default mode
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
schema-generate now builds content-aware schemas from the document's
section hierarchy instead of counting markdown syntax elements. Detects
key-value tables, data tables, link lists, and mixed content patterns
to produce schemas that reflect the actual document outline.

Old behavior preserved via --mode syntactic. Validator and visualization
tools pinned to syntactic mode for compatibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 18:49:50 +01:00
120ed89780 fix(proxy): catch markitdown missing-dependency errors with clean hint
When markitdown is installed but a format-specific sub-dependency is
missing (e.g. pdfminer-six for PDF), translate the raw traceback into
a DependencyMissingError with the correct install command.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 21:00:51 +01:00
9fa239c140 fix(proxy): register markitdown extractor unconditionally
Always register MarkitdownExtractor so it overrides specialized extractors
for all its extensions. When markitdown-no-magika is not installed, users
now see the correct install hint instead of the old pymupdf4llm message.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 20:52:07 +01:00
e4fbba8a57 feat(proxy): add markitdown as default proxy backend
Uses markitdown-no-magika (lighter fork without magika/onnxruntime) to
handle PDF, HTML, DOCX, PPTX, XLSX, XLS, CSV, JSON, and XML files.
Specialized extractors (pymupdf4llm, markdownify) remain as fallbacks
when markitdown is not installed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 20:48:47 +01:00
ac334c679d feat(proxy): add proxy file system for non-markdown source conversion
Introduces a new `markitect/proxy/` module with pluggable extractors that
convert non-markdown sources (PDF, HTML) into tracked markdown proxy files.
Proxy files preserve origin metadata (path, checksum, timestamp) so they
can be kept in sync when the original changes.

CLI commands: `proxy create`, `proxy update`, `proxy status`, `proxy extractors`.
Built-in extractors: PDF (pymupdf4llm), HTML (markdownify), Markdown (built-in).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 19:06:09 +01:00
69aea1ada7 refactor(version): separate version and release commands
`markitect version` now prints a clean version string (Unix style),
with -v for commit/branch/dirty. `markitect release` shows detailed
development status: commits since tag, local changes, upstream
divergence. No overlap between the two commands.

Replaces get_version_info()/get_release_info() with get_version()
and get_release_status(). Drops yaml output format from release
(json + text sufficient).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 17:49:14 +01:00
be3b4e3aae fix(version): resolve version dynamically from git in dev checkouts
When running from a git repo, use setuptools-scm at runtime to derive
the version from tags. Falls back to the static _version.py only when
not in a git repo (e.g. installed from wheel). This ensures
`markitect version` stays correct without requiring `pip install -e .`
after every tag.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 17:22:38 +01:00
ad23bb0b86 fix(version): normalize release info for CLI release command
Add _normalize_release_info() to ensure get_release_info() returns
keys expected by the CLI release command regardless of whether the
release-management capability is available.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 16:37:40 +01:00
5085c44de3 feat(llm): add llm-default and llm-preference commands, switch hardcoded default to gemini
Add TOML-based config resolution with 7-level priority chain:
CLI flags > env var > user preference > directory preference >
directory default > user default > hardcoded fallback.

New commands: llm-default (view/set/clear defaults), llm-preference
(view/set/clear preferences). Each shows only its own scope. llm-check
now displays source attribution for resolved provider/model.

Existing commands (llm-helper, llm-check) refactored to use
resolve_llm() instead of manual resolution. Hardcoded fallback
changed from openrouter/aurora-alpha to gemini/gemini-2.5-flash
due to persistent OpenRouter 502 errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 16:35:44 +01:00
4631a9f794 feat(llm): add qwen3-coder-next to catalog and Known Models column
Register qwen/qwen3-coder-next under the openrouter provider and extend
llm-catalog with a "Known Models" column so all cataloged models are
discoverable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 00:17:57 +01:00
269184f7a1 feat(llm): add llm-catalog and llm-check commands, rename helper → llm-helper
Consistent llm-* naming scheme for all LLM CLI commands. llm-catalog shows
provider metadata and key status; llm-check sends a minimal prompt to verify
connectivity.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 00:12:50 +01:00
69e2ec25ff feat(helper): add interactive Q&A helper command
Add `markitect helper <QUESTION>` CLI command that answers questions
about markitect using its own documentation as LLM context. Uses
OpenRouter with openrouter/aurora-alpha by default; model is
configurable via --model flag or MARKITECT_HELPER_MODEL env var.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 23:28:20 +01:00
41773f1320 feat(llm): add OpenAI adapter, entity archive policy, process chapters 5-7
Add OpenAIAdapter for the OpenAI chat completions API (apikey-chatgpt.txt
or OPENAI_API_KEY). Set default model to arcee-ai/trinity-large-preview:free
for the infospace pipeline and increase max_tokens from 4096 to 8192.

Reprocess chapter 05 with Trinity Large (was Gemini: 1 truncated entity,
now 19 complete entities). Process chapters 06 (Aurora Alpha, 10 entities)
and 07 (Trinity Large, 15 entities including regenerated violent-policy.md).
Canonical set now at 85 unique entities.

Add entity archive policy: entities are never silently deleted. Retired
entities move to output/entities/archive/ with a dated reason header.
New CLI option: --archive-entity <slug> --reason "...". The --list
output shows the archive count alongside the canonical set.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:39:44 +01:00
880c1d1374 feat(llm): add Gemini adapter and process book-1-chapter-05
Add GeminiAdapter calling Google's Generative Language REST API
(default model: gemini-2.5-flash). Register "gemini" as third
provider in the factory and CLI. Add rate-limit retry with
exponential backoff to the pipeline's _call_llm helper. Increase
default max_tokens from 2000 to 4096.

Process book-1-chapter-05 via Gemini free tier — 1 new entity
extracted (necessaries-conveniencies-and-amusements-of-life),
41 existing entities correctly skipped by dedup. Canonical set
now at 42 unique entities.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 22:54:37 +01:00
2d1282a61e feat(infospace): flat canonical entity set with cross-chapter deduplication
Restructure entity storage from per-chapter subdirectories to a flat
canonical set in output/entities/. Each entity exists as a single file;
duplicates across chapters are detected by slug collision and skipped
(first occurrence wins). Chapter views use {{ include }} transclusion
to reference shared entity files.

Add @{existing_entities} macro to extract-entities template so the LLM
knows which entities already exist and focuses on genuinely new ones.
Refactor _call_llm() from _execute_llm() for callers that handle their
own file I/O. 41 unique entities from 4 chapters (2 duplicates removed).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 22:24:20 +01:00
706981c39f fix(prompts): fix three infrastructure bugs in prompt dependency resolution
- ContentMacro: add __post_init__ to auto-derive raw_text when built
  programmatically, preventing str.replace("", X) corruption
- MacroParser: add @{target} shorthand syntax support mapped to REQUIRED kind,
  updating parse, has_macros, count_macros, and find_macro_positions
- Artifact: store content in model and SQLite DB, replace resolver placeholder
  with actual artifact content, add migration for existing databases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 20:53:02 +01:00
01b9596ce6 docs(examples): add infospace-with-history tutorial
Comprehensive walkthrough covering schema design, prompt templates,
artifact population, pipeline usage, LLM integration, git history
tracking, metrics, and how to complete the remaining 31 chapters.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 01:50:49 +01:00
ad84dd3a41 infospace: process book-1-chapter-04 via OpenRouter
All 3 stages (entities, mappings, analysis) auto-generated.
1m53s wall time, 9,478 tokens (real), ~$0.07 est. cost.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 01:42:05 +01:00
e806a701ca infospace: process book-1-chapter-03 with LLM integration
Auto-generated mappings and analysis via Claude Code CLI adapter.
Entities were already present from a previous session.

Stats: 5m04s wall time, ~51K estimated tokens, ~$0.35 estimated cost.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 01:32:24 +01:00
fecc2fd4fa feat(llm): add LLM integration module with OpenRouter and Claude Code adapters
Implements markitect/llm/ package with concrete LLMAdapter implementations:
- OpenRouterAdapter: HTTP via urllib with retry/backoff on 429/5xx
- ClaudeCodeAdapter: subprocess-based Claude CLI with stdin piping
- Factory pattern: create_adapter("openrouter") or create_adapter("claude-code")
- API key resolution chain: constructor > env var > project-root key file
- 42 unit tests, 2 integration tests (gated on API key / CLI availability)

Also adds the infospace-with-history example with Wealth of Nations VSM
analysis pipeline, templates, schemas, source chapters, and processed
output for chapters 1-2. process_chapters.py now supports --provider
and --model flags for automatic LLM-driven processing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 01:17:58 +01:00
360c3b1de2 feat(examples): add content-generator example demonstrating Prompt Dependency Resolution
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
This example demonstrates the full workflow of generating InfoTech primers
using MarkiTect's Prompt Dependency Resolution infrastructure.

Features demonstrated:
- Artifact creation and storage with content-based addressing
- PromptTemplate with @{macro} resolution across multiple spaces
- Automatic dependency tracking and graph construction
- Provenance tracing from outputs back to inputs
- Visualization export (Mermaid format)
- Incremental execution with change detection

Files added:
- generate_primers.py: Complete working example
- README.md: Quick start guide and architecture overview
- TUTORIAL.md: Comprehensive 500+ line tutorial
- templates/generate-primer.md: Template with macros
- artifacts/topics/: ETL and Microservices topic definitions
- artifacts/guidelines/: Authoring rules and research protocol
- prepdr/: Original manual system (preserved for reference)

Example output:
- Generates 2 primers (ETL, Microservices)
- Creates 8 artifacts across 4 information spaces
- Records 8 dependency edges in SQLite database
- Exports dependency graph visualization

Run with: cd examples/content-generator && python generate_primers.py

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-09 23:50:07 +01:00
8f54a5509e Included run manifest schema to check prompt dependency resolution against later
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
2026-02-09 20:38:07 +01:00
7b4bd461c9 feat(prompts): implement Phase 8 - Observability & Traceability (FR-11)
Complete implementation of Phase 8, the final phase of prompt dependency
resolution infrastructure, adding full observability and traceability.

## Features (FR-11)

### FR-11.1: Complete Artifact Provenance Tracing
- TraceabilityService: composition layer for full artifact lineage
- Trace any artifact to producing PromptTemplate, input artifacts,
  generator runs, and quality validation results
- ProvenanceTrace model with complete dependency chain reconstruction
- RunSummary and ArtifactLineage models for structured trace output

### FR-11.2: Recomputation Query Infrastructure
- PromptQueryService: cross-service complex queries
- Run history queries with template and status filters
- Stale artifact detection via impact debt analysis
- Dependency graph statistics (nodes, edges, cycles, roots, leaves)
- Content-based artifact lookups by digest

### Visualization Support
- GraphExporter: DOT (Graphviz) and Mermaid format export
- Supports all edge types (requires, generates, includes)
- Handles isolated nodes, linear chains, diamonds, and complex graphs

### CLI Commands (prompt group)
- `prompt trace <artifact_id>` - Full provenance trace as JSON
- `prompt graph <artifact_id>` - Dependency graph (DOT/Mermaid)
- `prompt runs` - List execution runs with filters
- `prompt debt` - Show impact debt and stale artifacts
- `prompt stats` - Dependency graph statistics

## Implementation

Source files (8):
- markitect/prompts/traceability/models.py - Trace data models
- markitect/prompts/traceability/service.py - TraceabilityService
- markitect/prompts/visualization/graph.py - Graph export
- markitect/prompts/queries/operations.py - PromptQueryService
- markitect/prompts/cli.py - Click CLI commands
- Package __init__.py files (3)

Tests (64 total, all passing):
- tests/unit/prompts/test_traceability_service.py (21 tests)
- tests/unit/prompts/test_visualization.py (14 tests)
- tests/unit/prompts/test_query_operations.py (12 tests)
- tests/integration/prompts/test_traceability_workflow.py (7 tests)
- tests/integration/prompts/test_prompt_cli.py (10 tests)

## Architecture

TraceabilityService is a composition layer that delegates to:
- DependencyQueryService (transitive dependency lookups)
- QualityValidator (validation history)
- IncrementalExecutionEngine (impact debt queries)
- Direct repository access (artifacts, edges)

No duplicate data storage - all data comes from existing Phase 1-7
infrastructure (artifact repo, dependency repo, validation DB, debt DB).

## Verification

All 2250 tests pass with 0 regressions.
Phase 8 completes the full 8-phase implementation roadmap.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-09 20:32:18 +01:00
704272644c feat(prompts): implement Phase 7 - Quality & Validation (FR-9, FR-10)
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Add quality gate framework with schema validation (JSON Schema via
jsonschema library), pattern validation (regex-based), multi-gate
QualityValidator with SQLite persistence, HaltingPolicyEngine with
budget/iteration/improvement checks, and RefinementLoop for iterative
execute-validate-halt cycles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 13:31:37 +01:00
bd1d05ba79 feat(prompts): implement Phase 6 - Incremental Execution (FR-7, FR-8)
Add change detection, structural diff-based impact analysis,
configurable-depth incremental recomputation with circular suppression,
and impact debt tracking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 13:18:27 +01:00
9ce157400e feat(prompts): implement Phase 5 - Dependency Tracking (FR-6)
Add directed dependency graph with cycle detection, topological sort,
and query service for finding dependents/dependencies transitively.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 13:18:18 +01:00
c56c92c815 feat(prompts): implement Phase 4 - Execution Engine (FR-4, FR-5)
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Implement three-stage execution lifecycle with idempotent runs and complete
provenance tracking via RunManifest.

Core Features:
- PromptRun model with execution lifecycle stages:
  1. Analysis: Template analysis and macro extraction
  2. Compilation: Macro resolution and context compilation
  3. Processing: LLM execution and output generation
- InputBundleHash for deterministic idempotency (FR-4.3)
- RunManifest for complete execution provenance (FR-5)
- LLMAdapter interface for pluggable model providers
- MockLLMAdapter for testing without API calls
- PromptExecutionEngine orchestrating full lifecycle

Idempotent Execution (FR-4.4):
- Calculate SHA-256 hash of complete input context
- Skip execution if identical hash exists
- Cache successful runs by hash
- Support force re-execution via config flag

RunManifest Tracking (FR-5.2):
- Template metadata (id, name, digest)
- Resolved input artifacts and digests
- Compiled prompt digest
- Model configuration
- Output artifacts
- Dependency edges for graph construction
- Timing metadata for performance analysis

Tests (27 passing):
- 17 execution model tests (config, bundle, runs, stages)
- 10 engine tests (execution, idempotency, errors, caching)

Implements:
- FR-4.1: Three-stage execution lifecycle
- FR-4.2: CompiledPrompt during compilation
- FR-4.3: InputBundleHash calculation
- FR-4.4: Skip execution for identical hashes
- FR-5.1: RunManifest persistence
- FR-5.2: Complete manifest contents
- FR-5.3: Nested run linking (foundation)

Files Created:
- markitect/prompts/execution/models.py
- markitect/prompts/execution/manifest.py
- markitect/prompts/execution/llm_adapter.py
- markitect/prompts/execution/engine.py
- migrations/prompts/003_create_runs_and_manifests.sql
- tests/unit/prompts/test_execution_models.py
- tests/unit/prompts/test_execution_engine.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 23:15:33 +01:00
5f463e5b20 feat(prompts): implement Phase 3 - Resolver Engine (FR-3)
Implement deterministic multi-space resolution with configurable search order.

Core Features:
- ResolutionContext and ResolutionResult for tracking resolution state
- MultiSpaceResolutionStrategy implementing FR-3.1 search order:
  1. Local InformationSpace
  2. Explicitly included InformationSpaces
  3. Default InformationSpace
  4. Team/Shared InformationSpace
- PromptResolver with macro resolution logic
- ContextCompiler for assembling resolved prompts
- ResolutionConfig for configurable resolution behavior

Resolution Behavior:
- Required macros fail if not found (FR-3.2)
- Optional macros resolve to empty (FR-3.3)
- Generate macros detected for deferred execution (FR-3.4)
- Deterministic search order with duplicate removal
- Partial compilation support for debugging

Tests (31 passing):
- 14 strategy tests (search order, duplicates, priority)
- 9 resolver tests (required, optional, generate, multi-space)
- 8 compiler tests (substitution, dependencies, digests)

Implements:
- FR-3.1: Deterministic resolution order
- FR-3.2: Required macro validation
- FR-3.3: Optional macro fallback
- FR-3.4: Generate macro detection
- FR-3.5: Max generation depth configuration

Files Created:
- markitect/prompts/resolver/models.py
- markitect/prompts/resolver/strategy.py
- markitect/prompts/resolver/resolver.py
- markitect/prompts/resolver/compiler.py
- migrations/prompts/002_create_resolution_config.sql
- tests/unit/prompts/test_resolution_strategy.py
- tests/unit/prompts/test_prompt_resolver.py
- tests/unit/prompts/test_context_compiler.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 22:45:46 +01:00
e6840fe696 feat(prompts): implement Phase 2 - Templates & Macros (FR-2)
Implement PromptTemplate models with ContentMacro parsing and analysis.

Core Features:
- PromptTemplate extending Artifact for template-specific operations
- ContentMacro model supporting REQUIRED, OPTIONAL, GENERATE kinds
- MacroParser for extracting macros from template content
- TemplateAnalyzer for dependency extraction and validation
- TemplateService for high-level template operations
- Template metadata for model hints and expected inputs

Macro Syntax:
- {{require:artifact-name}} - Required dependency
- {{optional:artifact-name}} - Optional dependency
- {{generate:template-name|param=value}} - Nested generation

Tests (38 passing):
- 18 template model tests (macros, templates, metadata)
- 20 parser tests (parsing, validation, parameters, aliases)

Implements:
- FR-2.1: PromptTemplate as content artifact with macros
- FR-2.2: ContentMacro detection and extraction
- FR-2.3: Required/Optional/Generate macro kinds

Files Created:
- markitect/prompts/templates/models.py
- markitect/prompts/templates/parser.py
- markitect/prompts/templates/analyzer.py
- markitect/prompts/services/template_service.py
- tests/unit/prompts/test_template_models.py
- tests/unit/prompts/test_macro_parser.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 22:34:22 +01:00
945544880d feat(prompts): implement Phase 1 - Foundation (FR-1)
Implement addressable artifacts with content-based identity and change detection.

Core Features:
- Artifact model with SHA-256 content digests
- ArtifactReference for cross-space addressing
- IArtifactRepository interface for pluggable storage
- SQLiteArtifactRepository implementation
- ArtifactService for high-level operations
- Content digest calculation utilities

Database:
- prompt_artifacts table with indexes
- Support for artifact metadata and types
- UNIQUE constraint on space_id+name

Tests (41 passing):
- 26 model tests (metadata, artifacts, references, digests)
- 15 repository tests (CRUD, queries, constraints)

Implements:
- FR-1.1: Unique addressability by name and ID
- FR-1.2: Content digest computation and storage
- FR-1.3: Cross-space artifact references

Files Created:
- markitect/prompts/models.py
- markitect/prompts/repositories/interfaces.py
- markitect/prompts/repositories/sqlite.py
- markitect/prompts/services/artifact_service.py
- migrations/prompts/001_create_artifacts_table.sql
- tests/unit/prompts/test_artifact_models.py
- tests/unit/prompts/test_artifact_repository.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 22:30:26 +01:00
cbde1dabc4 docs(prompts): add comprehensive implementation workplan
Create detailed 26-week workplan for Prompt Dependency Resolution system
implementing all 11 functional requirements across 8 phases:

- Phase 1-2: Foundation (artifacts, templates, macros)
- Phase 3-4: Resolution and execution engine with idempotent runs
- Phase 5-6: Dependency tracking and incremental recomputation
- Phase 7-8: Quality validation and observability/traceability

Includes database schemas, verification strategies, risk management,
and complete file structure for ~60 new modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 22:09:20 +01:00
6af04024d5 chore: added requirements for prompt dependency resolution
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
2026-02-08 21:29:54 +01:00
9ab135bb03 chore: moved information-space service to history
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
2026-02-08 21:26:54 +01:00
27847df9bd docs: add comprehensive INTRODUCTION.md
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Provides high-level overview of MarkiTect from value and functional perspective:
- What MarkiTect is and why it matters
- Core capabilities (Information Spaces, Schema Management, etc.)
- Practical use cases across different domains
- Key benefits for different user types
- Getting started guidance
- Philosophy and design principles

Focuses on user value and functionality without implementation details.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 18:29:14 +01:00
9061b2fd85 docs: mark Information Space Service workplan as complete
All 8 phases successfully implemented:
- Phase 0-1: Foundation (66 tests)
- Phase 2: Event System (19 tests)
- Phase 3: Persistent Transclusion (47 tests)
- Phase 4: HTML Rendering (60 tests)
- Phase 5: Directory Sync (45 tests)
- Phase 6: API Layer (38 tests)
- Phase 7: Composability (57 tests)
- Phase 8: Git History (43 tests)

Total: 421 passing tests
Status: Ready for production use

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 18:06:31 +01:00
4588cbeee8 feat(spaces): implement Phase 8 Git History Tracking
Implements optional git-based version control for information spaces:
- HistoryConfig model for configuring history tracking
- Commit, Branch, HistoryEntry, DiffResult models
- IHistoryBackend and IHistoryQuery interfaces
- GitHistoryBackend using git CLI for version control
- GitHistoryEventHandler for event-driven auto-commits
- HistoryEventCoordinator for managing space history
- HistoryQueryService for high-level history queries
- Automatic commits on DOCUMENT_ADDED/REMOVED/CONTENT_CHANGED events
- Support for:
  * Commit log with pagination and filtering
  * Diff between versions
  * File content at specific versions
  * Branch creation and switching
  * Version restoration
  * Uncommitted changes detection
- 43 comprehensive unit tests with git availability checks

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 18:03:35 +01:00
727ce4d3c5 feat(spaces): implement Phase 7 Composability
Implements space composition and inheritance features:
- SpaceReference model for space-to-space references (includes, extends, links_to, composed_of)
- Variable inheritance through parent chain with local override
- Config inheritance with source tracking
- Access control models (SpacePermission, SpaceRole, AccessLevel)
- InheritanceResolver for walking parent chains
- AccessControlService for permission management
- ComposableSpaceService integrating all composability features
- Circular reference detection for EXTENDS references
- SQLite repositories for references and permissions
- 57 comprehensive unit tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 17:41:40 +01:00
7de57a389d feat(spaces): implement Phase 6 API Layer
Implements API layer for Information Spaces:
- GraphQL schema types for spaces, documents, variables
- GraphQL queries and mutations for space operations
- CLI command group with all space management commands
- Resolver functions connecting GraphQL to SpaceService
- 38 unit tests for API components

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 12:29:11 +01:00
535b83996b feat(spaces): implement Phase 5 Directory Sync Mode
Implements directory synchronization for Information Spaces:

- SpaceDirectoryExporter: Export space to directory structure
  - Multiple variants: flat, hierarchical, by_path
  - Manifest generation for reimport
  - Incremental export (skip unchanged files)
  - Metadata file export
  - IncrementalExporter for change detection

- DirectorySpaceImporter: Import directory content into space
  - Recursive directory scanning
  - Multiple file pattern support
  - Conflict detection with strategies (skip/overwrite/rename)
  - Manifest-based import for intelligent reimport
  - Structure preservation in space paths

- BidirectionalSyncCoordinator: Two-way sync with conflict detection
  - Sync directions: space-to-directory, directory-to-space, bidirectional
  - Conflict resolution strategies: space_wins, directory_wins, newer_wins, manual, skip
  - Dry-run mode for preview
  - Orphan cleanup option
  - Event emission for progress tracking

45 unit tests covering all sync components.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 12:11:37 +01:00
2a5c265458 feat(spaces): implement Phase 4 HTML Rendering Mode
Implements HTML rendering system for Information Spaces:

- SpaceRenderer: Abstract base class for renderers
- RenderConfig: Configuration for format, theme, TOC, etc.
- RenderResult: Immutable result with content hash and metadata
- ThemeConfig: Layered theme system with customization
- CompositeRenderer: Multi-format renderer delegation

- MarkdownToHTMLRenderer: Full markdown-to-HTML conversion
  - Theme support (github, dark, minimal, academic)
  - Code block handling
  - Link target="_blank" for external links
  - Table of contents generation
  - Heading ID generation for navigation
- HTMLRendererFactory: Factory for common renderer configurations

- SpaceRenderingService: Orchestration layer
  - Transclusion variable substitution
  - Render caching with automatic invalidation
  - Event emission (RENDER_STARTED, RENDER_COMPLETED, RENDER_FAILED)
  - Batch rendering support
  - Statistics tracking
- SpaceRenderingServiceBuilder: Fluent builder pattern

60 unit tests covering all components.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 08:42:27 +01:00
7da77396a9 feat(spaces): implement Phase 3 Persistent Transclusion Context
Implements persistent transclusion context for Information Spaces:

- ScopedVariables: Variable scope layers (request > document > space)
- SpaceTransclusionContext: Extends TransclusionContext with DB persistence
- CrossSpaceResolver: Resolve references across space boundaries
- ReferenceGraph: Track document dependencies for cache invalidation
- PersistentReferenceGraph: Repository-backed reference tracking
- RenderCache: Cache rendered output with invalidation support
- CacheInvalidator: Event-driven cache invalidation using reference graph

Key features:
- Variable precedence: request overrides document overrides space
- Reference tracking during transclusion processing
- Transitive dependent calculation for cache invalidation
- Event bus integration for automatic invalidation on content changes

47 unit tests covering all components.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 08:36:50 +01:00
0a494b2011 feat(spaces): implement Phase 2 Event System
Week 4 - Event Infrastructure:
- Create SpaceEventType enum with 18 event types covering space lifecycle,
  document operations, variables, references, rendering, sync, and cache
- Create SpaceEvent dataclass with serialization/deserialization
- Create EventBus with sync/async handler support, priority ordering,
  global handlers, and optional event history
- Add event factory functions for common events

Week 5 - Event Integration:
- Wire EventBus into SpaceService as optional dependency
- Emit events for all space operations:
  - SPACE_CREATED, SPACE_UPDATED, SPACE_DELETED, SPACE_ACTIVATED, SPACE_ARCHIVED
  - DOCUMENT_ADDED, DOCUMENT_REMOVED, DOCUMENT_MOVED, DOCUMENT_CONTENT_CHANGED
  - VARIABLE_SET, VARIABLE_DELETED
- Create integration tests for event propagation patterns

Test coverage: 187 tests total
- 43 unit tests for event system
- 20 integration tests for event propagation
- 124 existing tests continue to pass

Capabilities delivered:
- CAP-010: SpaceEvent base with type, payload, timestamp
- CAP-011: EventBus with in-process publish/subscribe
- CAP-012: Event handlers registry with priority support
- CAP-013: Change detection via content hash comparison

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 07:41:47 +01:00
9b12875681 feat(spaces): implement Phase 0-1 of Information Space Service
Phase 0 - Project Organization:
- Create docs/PROJECT_STRUCTURE.md documenting codebase layout
- Create markitect/core/ with parser, serializer, document_manager, workspace
- Create markitect/schema/ consolidating 6 schema_*.py modules
- Create markitect/storage/ with database module
- Maintain backward compatibility via re-exports from original locations
- Add docs/roadmap/information-space-service/ with README and WORKPLAN

Phase 1 - Foundation (Weeks 1-3):
- Week 1: Core domain models (InformationSpace, SpaceDocument, SpaceConfig,
  SpaceMetadata, SpaceVariable, TransclusionReference, SpaceStatus)
- Week 2: Repository layer with interfaces (ISpaceRepository,
  IDocumentAssociationRepository, IVariableRepository, IReferenceRepository)
  and SQLite implementations with foreign key cascade deletes
- Week 3: SpaceService orchestration layer with full CRUD, document,
  variable, and reference tracking operations

Test coverage: 124 tests (25 model + 63 repository + 36 integration)

Capabilities delivered:
- CAP-001: InformationSpace entity with lifecycle management
- CAP-002: SpaceRepository CRUD with SQLite backing
- CAP-003: Document-Space associations with path-based organization
- CAP-004: Space metadata and configuration schemas
- CAP-005: Database schema with migrations

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 02:02:46 +01:00
6ebcc0f60e docs: Added dependencies documentation
Some checks failed
Test Suite / code-quality (push) Has been cancelled
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
2026-01-23 13:25:12 +01:00
72519bf83d chore: close release-management-optimization topic
- Move topic from roadmap/ to history/
- Add DONE.md with comprehensive completion summary
- Topic fully complete with all 9 optimizations implemented
- Exceeded original scope (Stages 1-2 + all of Stage 3)
- Ready for archive
2026-01-06 22:54:10 +01:00
1f9d618777 docs: prepare CHANGELOG for v0.11.0 release
Some checks failed
Test Suite / security-scan (push) Has been cancelled
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
2026-01-06 22:29:02 +01:00
ce11c03326 chore: update Changelog
Some checks failed
Test Suite / security-scan (push) Has been cancelled
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
2026-01-06 22:26:03 +01:00
0ade4798f3 docs: update PROGRESS.md with completion of all 9 optimizations 2026-01-06 21:51:35 +01:00
843f579305 feat: implement optimization #9 - release notes from CHANGELOG
Add release notes extraction from CHANGELOG for publishing:

- Create ChangelogParser class to extract version sections from CHANGELOG
- Support multiple output formats: markdown, plain text, HTML
- Add 'release notes VERSION' CLI command to extract notes
- Auto-detect latest version if not specified
- Support piping to gh/gitea release commands
- Save to file with --output option
- Plain text format removes markdown formatting
- HTML format converts markdown to HTML

This streamlines creating release notes for GitHub/Gitea releases
by extracting CHANGELOG content automatically.

Usage:
  release notes 0.10.0                    # Extract markdown notes
  release notes                           # Latest version
  release notes 0.10.0 --format plain    # Plain text
  release notes 0.10.0 -o notes.md       # Save to file
  release notes 0.10.0 | gh release create v0.10.0 -F -

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-06 21:49:09 +01:00
7515b9c0e5 feat: implement optimization #8 - schema auto-ingestion
Add automated schema ingestion from markitect/schemas/ directory:

- Create auto_ingest_schemas() function in schema_loader module
- Automatically detect and ingest .md schema files from schemas/
- Skip schemas that are already ingested in database
- Return detailed results with ingested/skipped/failed lists
- Add 'markitect schema-auto-ingest' CLI command
- Support verbose mode for detailed progress reporting
- Useful for post-install setup and development workflows

This eliminates the manual step of running schema-ingest for each
bundled schema file, streamlining schema management.

Usage:
  markitect schema-auto-ingest           # Ingest all new schemas
  markitect schema-auto-ingest --verbose # Show detailed progress

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-06 21:34:46 +01:00
7f696582a9 feat: implement optimization #7 - release summary auto-generation
Add automated release summary document generation:

- Create SummaryGenerator class to generate comprehensive release summaries
- Extract CHANGELOG sections for specific versions automatically
- Calculate git statistics (commits, files changed, insertions, deletions)
- List build artifacts from dist/ directory with sizes
- Include validation results in summary
- Add 'release summary VERSION' CLI command to generate summaries
- Support custom output paths with --output option
- Auto-detect project name from pyproject.toml
- Include contributor information from git log

This automates the manual task of creating release documentation,
ensuring consistent and comprehensive release summaries.

Usage:
  release summary 0.10.0                           # Generates RELEASE_SUMMARY_v0.10.0.md
  release summary 0.10.0 --output docs/v0.10.0.md  # Custom output path

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-06 21:32:28 +01:00
5fea98b068 feat: implement optimization #5 - CHANGELOG section generation
Add automated CHANGELOG section preparation for releases:

- Create ChangelogEditor class for programmatic CHANGELOG.md editing
- Implement create_version_section() to create new release sections
- Automatically move [Unreleased] content to new version section
- Add 'release prepare VERSION' CLI command to prepare CHANGELOG
- Validate CHANGELOG after edit to ensure correctness
- Support custom release dates with --date option
- Provide helpful feedback about content movement

This streamlines release preparation by automating the manual task of
creating version sections and moving unreleased changes.

Usage:
  release prepare 0.11.0            # Uses today's date
  release prepare 0.11.0 --date 2026-01-15

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-06 21:28:46 +01:00
0b5098370a feat: implement optimization #4 - version-tag consistency check
Add version-tag consistency validation to prevent mismatched releases:

- Integrate validate_changelog_version() into create_tag() workflow
  to ensure CHANGELOG has version section before creating git tag
- Add check_version_consistency() method to ReleaseManager for
  manual consistency verification
- Add 'release check-consistency --version X.Y.Z' CLI command to
  verify CHANGELOG and git tag alignment
- Prevent tag creation if CHANGELOG missing version section
- Provide helpful tips when validation fails

This ensures git tags and CHANGELOG versions stay synchronized,
preventing incomplete or inconsistent releases.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-06 21:14:33 +01:00
599de22f59 feat: implement optimization #3 - CHANGELOG validation in release flow
Add comprehensive CHANGELOG validation to release validation process:

- Add _validate_changelog() method that validates CHANGELOG.md against
  changelog-schema-v1.0.md using markitect validate --semantic
- Add validate_changelog_version() to check version section exists with
  proper date format and Unreleased section
- Add check_version_tag_consistency() to verify CHANGELOG versions
  match git tags
- Integrate CHANGELOG validation into validate_release_state()
- Add CHANGELOG-specific recommendations to _get_recommendations()

This prevents releases with invalid or inconsistent CHANGELOG files,
catching format errors before they become problems.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-06 21:11:40 +01:00
23521ad6ae docs: add optimization implementation progress tracker
Created comprehensive progress tracking document for optimization
implementation showing 2/9 optimizations complete (22%).

**Completed** (2 hours):
-  Optimization #1: Git status unpushed tags detection
-  Optimization #2: Automated tag pushing control

**Remaining** (11.5 hours):
-  #3: CHANGELOG validation (2 hours) - NEXT
-  #4: Version-tag consistency (1 hour) - NEXT
-  #5: CHANGELOG section generation (3 hours)
-  #6: Explicit version command (30 min)
-  #7: Release summary auto-generation (2 hours)
-  #8: Schema auto-ingestion (1 hour)
-  #9: Release notes from CHANGELOG (2 hours)

**Strategy**: Phased implementation
- Phase 1 (HIGH): 50% complete (2/4 done)
- Phase 2 (MEDIUM): Not started (0/3)
- Phase 3 (LOW): Not started (0/2)

**Next Session**: Implement optimizations #3-4 (3 hours)
2026-01-06 17:29:06 +01:00
0d276e8589 feat: implement optimization #2 - automated tag pushing control
Added --push/--no-push flag to release tag command for explicit control
over tag pushing behavior.

**Implementation**:
- Added --push/--no-push flag to CLI tag command (default: --push)
- Updated ReleaseManager.create_tag to accept push parameter
- Updated GitManager.create_tag to conditionally push based on flag
- Maintains backward compatibility (defaults to pushing)

**Usage**:
```bash
# Default behavior - creates and pushes tag
release tag --version 0.11.0

# Explicit push (same as default)
release tag --version 0.11.0 --push

# Create tag but don't push (manual push later)
release tag --version 0.11.0 --no-push
```

**Output when --no-push used**:
```
 Tag v0.11.0 created
💡 Push tag with: git push origin v0.11.0
```

**Benefits**:
- Makes push behavior explicit and controllable
- Prevents accidental pushes in some workflows
- Defaults to safe behavior (automatic push)
- Helpful reminder shown when --no-push used

**Files Modified**:
- capabilities/release-management/src/release_management/cli/main.py
- capabilities/release-management/src/release_management/core/manager.py
- capabilities/release-management/src/release_management/git/manager.py

Optimizations completed: 2/9 (High Priority)
2026-01-06 17:27:55 +01:00
587d2f5889 feat: implement optimization #1 - unpushed tags detection
Added unpushed tag detection to release status command to prevent
forgotten tag pushes (the critical issue from v0.10.0 release).

**Implementation**:
- Added `get_unpushed_tags()` method to GitManager
- Compares local tags with remote tags (git ls-remote)
- Handles annotated tags correctly (strips ^{} suffix)
- Added unpushed_tags to repository status dict

**CLI Enhancement**:
- `release status` now shows unpushed tags with warning emoji
- Lists all unpushed tags
- Provides helpful command to push them

**Output Example**:
```
⚠️  Unpushed Tags: 2 tag(s) not pushed to origin
    - v0.9.0
    - v0.10.0

💡 Push tags with: git push origin v0.9.0 v0.10.0
   Or push all tags: git push --tags
```

**Testing**: Verified with current repo (no unpushed tags after push)

**Files Modified**:
- capabilities/release-management/src/release_management/git/manager.py
- capabilities/release-management/src/release_management/cli/main.py

**Documentation**: Added comprehensive IMPLEMENTATION_PLAN.md with
all 9 optimizations detailed (13.5 hours total estimated)

This solves the #1 critical issue from OPTIMIZATION_ASSESSMENT.md.
2026-01-06 17:26:09 +01:00
bf4767d06b docs: add git status unpushed tags optimization
Added critical optimization #1 based on v0.10.0 release experience:

**Issue**: git status doesn't show unpushed tags, leading to forgotten tag pushes
**Impact**: v0.9.0 and v0.10.0 tags weren't pushed, plus older version tags
**Solution**: Enhanced release status or git hook to show unpushed tags

Total optimizations identified: 9 (was 8)
- High Priority: 4 (added unpushed tags visibility)
- Medium Priority: 3
- Low Priority: 2

Ready to implement all optimizations systematically.
2026-01-06 17:22:09 +01:00
75c8f8c325 docs: add release summary and optimization assessment
Added comprehensive documentation to release-management-optimization topic:

**RELEASE_SUMMARY.md**:
- Complete v0.10.0 release documentation
- Build artifacts, testing results, validation status
- Git statistics and file changes
- Next steps and manual actions required

**OPTIMIZATION_ASSESSMENT.md**:
- Post-release analysis of what worked vs. issues
- Identified 8 optimization opportunities across 3 priority levels
- Detailed Stage 3 implementation recommendations
- Three options for next steps (Complete Stage 3, Quick Wins, or Move On)

**Key Finding**: Forgot to push tags (git push doesn't include tags by default)
**Action Required**: `git push --tags` to push v0.9.0 and v0.10.0 tags

**Recommendation**: Implement Stage 3 (2 hours) for automated validation
and tag pushing to prevent similar issues in future releases.
2026-01-06 17:16:15 +01:00
6852ad915e docs: document completion of release-management-optimization Stages 1-2
Some checks failed
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Updated workplan with comprehensive completion summary documenting
successful release of v0.10.0 following Standard Track (Stages 1-2).

**Completion Summary**:
- Stage 1: Critical Fixes  (~45 min)
  - Fixed setuptools-scm configuration
  - Created v0.9.0 retroactive tag
  - Prepared CHANGELOG for v0.10.0

- Stage 2: CHANGELOG Schema  (~90 min)
  - Created changelog-schema-v1.0.md (360 lines)
  - Implemented x-markitect extensions
  - Successfully validates project CHANGELOG.md
  - All semantic checks passing

**Release**: v0.10.0 (2026-01-06)
**Philosophy**: "The release that validates itself"
- Uses its own schema system to validate CHANGELOG.md
- Perfect showcase of schema evolution practical value

**Deferred Work**:
- Stage 3: Release capability enhancements (future)
- Stage 4: Schema system extensions (not needed)

Updated status from "Planning" to "Stages 1-2 Complete, v0.10.0 Released"
2026-01-06 16:25:17 +01:00
c4ee5cc645 feat: add changelog schema for Keep a Changelog validation
Created comprehensive changelog-schema-v1.0.md to validate CHANGELOG.md
files following the Keep a Changelog format. This schema demonstrates
the practical application of the schema evolution system.

**Schema Features**:
- Section validation: Enforces [Unreleased] section presence
- Version format validation: [X.Y.Z] - YYYY-MM-DD pattern
- Semantic versioning compliance
- ISO 8601 date format checking
- Change type subsections: Added, Changed, Deprecated, Removed, Fixed, Security
- Content pattern matching via x-markitect-content-control extensions
- Structural validation via JSON Schema properties

**Validation Results**:
 Successfully validates project CHANGELOG.md
 All section requirements met (7 sections checked, 11 found)
 All content requirements met
 All semantic checks passing

**Implementation Notes**:
- H1 "Changelog" title validated via JSON Schema structural checks
- H2 sections validated via x-markitect-sections classifications
- SectionValidator limitation: Only checks H2+ headings, not H1
- Workaround: Structural validation covers H1 title requirement

**Philosophy**: "The release that validates itself"
- v0.10.0 uses its own schema system to validate its CHANGELOG
- Perfect showcase of schema evolution practical value
- Demonstrates x-markitect extensions in real-world use case

**Stage 2 Complete** per release-management-optimization workplan.

Files:
- markitect/schemas/changelog-schema-v1.0.md (new)
- CHANGELOG.md (documented new schema)
2026-01-06 13:31:02 +01:00
061ba88206 fix: resolve version detection and prepare v0.10.0 release
**Critical Fixes for v0.10.0 Release**:

1. **Fixed setuptools-scm Configuration** (pyproject.toml):
   - Added git_describe_command with --match 'v*' pattern
   - Prevents setuptools-scm from parsing non-version tags
   - Resolves "markitect --version" returning "unknown"
   - Version detection now works correctly (0.9.1.dev76)

2. **Retroactively Created v0.9.0 Git Tag**:
   - Tagged commit b9c1b90 from 2025-11-14
   - Maintains version history integrity
   - CHANGELOG documented v0.9.0 but tag was missing
   - Enables proper version progression to v0.10.0

3. **Prepared CHANGELOG.md for v0.10.0 Release**:
   - Created [0.10.0] - 2026-01-06 section
   - Moved all Unreleased content to v0.10.0
   - Documented version detection fixes
   - Documented v0.9.0 retroactive tag creation

**Issue Identified**: Non-version git tags (e.g.,
"testdrive-jsui-migration-phase4-complete") were causing
setuptools-scm to crash with AssertionError.

**Solution**: Configure git describe to only match version tags
using --match 'v*' pattern, filtering out non-version tags.

**Result**: Version command now works correctly, showing
development version based on v0.9.0 + 76 commits.

**Next Step**: Ready to proceed with Stage 2 (CHANGELOG schema)
per release-management-optimization workplan.
2026-01-06 13:22:45 +01:00
4e9117ddcb plan: create release-management-optimization roadmap topic
Created comprehensive staged workplan for enhancing release management
infrastructure with robust validation using the schema system.

**Critical Issues Identified**:
- setuptools-scm missing tag_regex configuration
- markitect --version returns 'unknown' instead of actual version
- CHANGELOG shows v0.9.0 (2025-11-14) but git tag never created
- No validation for CHANGELOG format or version-tag consistency

**Solution Approach**:
Create changelog-schema-v1.0.md to validate Keep a Changelog format,
demonstrating schema evolution in real-world use case.

**Staged Workplan**:
- Stage 1 (45 min): Critical fixes to unblock v0.10.0 release
- Stage 2 (2.5 hrs): CHANGELOG schema creation and validation
- Stage 3 (2 hrs): Release capability enhancements
- Stage 4 (optional): Schema system extensions

**Showcase Feature**: 'The release that validates itself'
- v0.10.0 uses its own schema system to validate its CHANGELOG
- Perfect demonstration of schema evolution practical value

**Next Version**: v0.10.0 (not v0.9.0)
- CHANGELOG already shows v0.9.0 as released
- Must maintain version history integrity
2026-01-06 13:18:39 +01:00
5e3646fdff feat: complete schema-evolution topic with ADR schema and markdown support
This commit closes the schema-evolution topic (260105) by adding the final
deliverable (ADR schema) and fixing markdown schema support across commands.

**ADR Schema Created**:
- Comprehensive Architecture Decision Record validation schema
- 12 section classifications (7 required, 2 recommended, 2 optional, 3 improper/discouraged)
- Content pattern validation for ADR formatting rules (status dates, decision statements, rationale structure)
- Quality metrics for completeness (word counts, sentence counts)
- Follows title case naming convention (Status, Context, Decision, etc.)

**Markdown Schema Support Fixed**:
- Fixed `markitect validate` command to support .md schemas
  - Added load_schema_from_path() for both .json and .md files
  - Updated structural and semantic validation to use schema dict
- Fixed `markitect generate-stub` command to support .md schemas
  - Uses load_schema_from_path() instead of direct JSON loading
- Created DocumentWrapper class in semantic_validator.py
  - Extracts headings from AST tokens (heading_open, inline)
  - Provides get_headings_by_level() interface expected by validators
  - Enables section validation to work with real documents

**Topic Closure**:
- Updated SCHEMA_EVOLUTION_WORKPLAN.md with completion summary
  - Phases 1-3: 100% complete (via Schema-of-Schemas and Semantic Validation)
  - Phase 4: Deferred as future enhancement (15-20 sessions)
  - Phase 5: 70% complete (docs done, CI/CD templates deferred)
- Created DONE.md with comprehensive task checklist
- Generated ADR template stub (examples/templates/adr-template.md)
- Moved topic from roadmap/ to history/260105-schema-evolution/

**Files Changed**:
- markitect/cli.py: Added markdown schema support to validate and generate-stub
- markitect/semantic_validator.py: Added DocumentWrapper class for AST parsing
- markitect/schemas/adr-schema-v1.0.md: New ADR validation schema (560 lines)
- examples/templates/adr-template.md: Generated ADR template stub
- history/260105-schema-evolution/: Moved completed topic to history

**Status**: Schema evolution topic successfully closed with ADR schema as final deliverable.
All schema commands now support markdown schemas. Section validation working correctly.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-06 12:32:38 +01:00
fc828a345b docs: standardize on yymmdd- timestamp prefix format
Some checks failed
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Naming Convention Updates:
- Renamed history/2026-01-06-semantic-document-validation → history/260106-semantic-document-validation
- Documented yymmdd- format convention in history/README.md and roadmap/README.md
- Updated all date references in WORKPLAN.md and DONE.md
- Fixed SCHEMA_MANAGEMENT_GUIDE.md references to use yymmdd- format

Convention Details:
- Format: yymmdd-topic-name (e.g., 260106-semantic-document-validation)
- Benefits: Concise while maintaining chronological sorting
- Examples documented in both README files
- Applies to both roadmap/ and history/ directories

This establishes a consistent timestamp prefix convention that Claude and its agents should follow.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-06 03:57:42 +01:00
4d72ee8032 chore: close semantic validation topic and move to history
Repository Cleanup:
- Moved roadmap/20260106-semantic-document-validation → history/2026-01-06-semantic-document-validation
- Added completion summary to WORKPLAN.md documenting all 6 phases
- Created DONE.md with detailed list of accomplished tasks
- Documented all deliverables, commits, and success metrics

Topic Status: COMPLETED on 2026-01-06
- All phases complete: Section, Content, Link validation
- 25 tests passing (100% coverage)
- Full documentation and CLI integration
- Production ready

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-06 03:50:57 +01:00
689fb21774 docs: update CHANGELOG with LinkValidator feature
Some checks failed
Test Suite / security-scan (push) Has been cancelled
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Added link validation details to semantic validation entry:
- Internal link validation (fragments and file paths) by default
- External link validation with --check-links flag (opt-in)
- Email validation for mailto: links
- Updated test coverage: 25 tests (16 section/content + 9 link)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-06 03:41:35 +01:00
20c0cfece7 feat: add LinkValidator for semantic link validation (Phase 3)
Implement comprehensive link validation as part of semantic validation:

Core Features:
- Link classification: internal, external, fragment, email
- Internal link validation: fragment anchors and file paths
- External link validation: HTTP/HTTPS with configurable timeout
- Email validation: mailto: link format checking
- Fragment policy enforcement: allow/disallow fragment identifiers

Link Validator:
- markitect/validators/link_validator.py - Full link validation implementation
- Supports x-markitect-content-control.link_validation configuration
- Default: check internal links, skip external (fast)
- Opt-in external checking with --check-links flag

Integration:
- Updated SemanticValidator to include link_result in reports
- CLI already supports --check-links flag (line 1629 in cli.py)
- Link validation runs by default for internal links (fast)
- External link checking requires explicit --check-links flag

Test Coverage:
- Added 9 comprehensive tests for LinkValidator
- Tests cover: classification, broken links, fragments, email, statistics
- All 25 semantic validator tests passing (100%)

Documentation:
- Updated SCHEMA_MANAGEMENT_GUIDE.md with link validation section
- Added examples for broken links and external link checking
- Documented link types, validation rules, and configuration

Statistics Tracking:
- Links checked, internal/external/fragment/email counts
- Detailed error/warning reporting with line numbers
- Integration with existing semantic validation reporting

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-06 03:41:03 +01:00
0d78837a53 docs: add semantic validation feature to CHANGELOG
Document the complete semantic validation system in the [Unreleased] section:
- Section classification enforcement (required/recommended/optional/discouraged/improper)
- Content pattern validation with regex matching
- Quality metrics checking (word/sentence counts)
- Modular validator architecture
- CLI integration with --semantic, --strict, --check-links flags
- 16 tests with 100% pass rate
- Complete documentation in SCHEMA_MANAGEMENT_GUIDE.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-06 03:30:58 +01:00
2836ae14de docs: add semantic validation guide to schema management
Adds comprehensive documentation for semantic document validation:

New Section: Document Validation (Semantic)
- Explains structural vs semantic validation
- Lists what is validated (sections, patterns, quality metrics)
- Shows validation output format
- Provides common validation scenarios with examples

Content:
- How to validate documents against schemas
- Section classification enforcement (required, recommended, etc.)
- Content pattern matching (required, forbidden, discouraged)
- Quality metrics (word counts, sentence counts)
- Usage examples with --semantic, --strict flags
- Error and warning examples

Location: docs/SCHEMA_MANAGEMENT_GUIDE.md (after Schema Validation section)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-06 03:28:28 +01:00
5264a6083c feat: enhance validate command with semantic validation
Integrates SemanticValidator into CLI validate command:

New Options:
- --semantic/--no-semantic (default: True) - Enable/disable semantic validation
- --check-links - Enable link validation (requires semantic validation)
- --strict - Treat warnings as errors (fail on WARNING-level issues)

Features:
- Automatically detects x-markitect extensions in schema
- Runs semantic validation alongside structural validation
- Combines results with clear separation in output
- Maintains full backward compatibility (--no-semantic for classic mode)
- Supports .md schema files with embedded JSON
- Graceful degradation: semantic validation errors don't crash command

Example Usage:
  # Full validation (structural + semantic)
  markitect validate doc.md --schema manpage-schema-v1.0.md

  # Strict mode (warnings = errors)
  markitect validate doc.md --schema schema.md --strict

  # Classic mode (structural only)
  markitect validate doc.md --schema schema.json --no-semantic

Output Format:
- Shows structural validation results first
- Then semantic validation results (sections, content)
- Clear summary with error/warning counts
- Exit codes: 0=pass, 1=fail (respects --strict flag)

Integration: cli.py:1493-1668

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-06 03:27:39 +01:00
a969c5de47 feat: add semantic document validator for x-markitect extensions
Implements semantic validation to complement existing structural validation:

Phase 1 & 2 Complete:
- SemanticValidator: Main validator orchestrating sub-validators
- SectionValidator: Enforces section classifications (required, recommended,
  optional, discouraged, improper) from x-markitect-sections
- ContentValidator: Validates content patterns, forbidden patterns, and
  quality metrics (word counts, sentence counts) from x-markitect-content-control

Features:
- Pattern matching with regex for required/forbidden/discouraged patterns
- Word count and sentence count validation
- Detailed error reporting with severity levels (ERROR, WARNING)
- Support for section alternatives (e.g., FLAGS vs OPTIONS)
- Comprehensive test coverage (16 tests, 100% passing)

Architecture:
- Complements existing SchemaValidator (structural AST validation)
- Clean separation: validators/ package for modular validators
- Semantic validation focuses on x-markitect-* extensions
- LinkValidator planned for Phase 3 (optional --check-links)

Next: Phase 4 - CLI integration to enhance 'markitect validate' command

Workplan: roadmap/20260106-semantic-document-validation/WORKPLAN.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-06 03:24:32 +01:00
f27eea6b5b chore: update kaizen-agentic submodule after rebase
Some checks failed
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Updated submodule reference after rebasing local commits on top of
remote changes. Local commits for project agent and TODO.md integration
now applied after remote updates to keepaTodofile and keepaContributingfile
agents.

Rebased commits:
- afc038d: agent: updated kaizen project agent
- 4b02ec5: feat: update project-management agent for TODO.md integration

Remote commits integrated:
- d372aea: Update agents/agent-keepaContributingfile.md
- 850a09e: Update agents/agent-keepaTodofile.md
2026-01-05 23:39:43 +01:00
ae2e8ee4a7 agent: updated kaizen project agent
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
2026-01-05 23:31:35 +01:00
b10d2fd3d0 agent: project-assistent update with roadmap and history
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
2026-01-05 23:18:45 +01:00
92719ff424 chore: updated header comments for TODO and CHANGELOG 2026-01-05 22:32:37 +01:00
9026646594 chore: redundant TODO.html removed
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
2026-01-05 22:02:38 +01:00
77415bfad7 chore: cleanup of history file
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
2026-01-05 22:01:04 +01:00
5e147865f8 chore: roadmap cleanup 2026-01-05 20:37:18 +01:00
3003b9b8da chore: archive completed schema-of-schemas implementation
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Moved schema-of-schemas planning artifacts from roadmap to history
with datestamp prefix, marking completion of all 6 implementation phases.

**Changes:**
- Moved roadmap/schema-of-schemas/ → history/2026-01-05-schema-of-schemas/
- Updated all documentation references to new location
- Marked implementation as completed in TODO.md
- Updated CHANGELOG.md to reflect archived status

**Implementation Summary:**
All 6 phases completed successfully:
- Phase 1: Filename validation (50 tests)
- Phase 2: Markdown schema loader (35 tests)
- Phase 3: Schema-for-schemas metaschema (12 tests)
- Phase 4: Schema migration (2 migrated, 3 deleted)
- Phase 5: CLI enhancements (multi-schema validation)
- Phase 6: Integration testing and documentation

**Deliverables:**
- 97 unit tests (100% passing)
- 4 production schemas in registry
- Comprehensive user documentation
- Updated examples (manpages, terminology)
- Complete schema management system

The schema-of-schemas topic is now complete and archived for
historical reference.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-05 14:13:48 +01:00
d32dc41315 docs: update manpage and terminology examples to schema-of-schemas standard
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Updated example documentation to use the new schema-of-schemas standard
with markdown schema format and multi-schema validation commands.

**Manpage Example Updates:**
- Changed schema reference from markdown-manpage-schema.json to manpage-schema-v1.0.md
- Updated all commands to use new multi-schema validation syntax
- Added examples of number-based validation (markitect schema-validate 2)
- Added examples of batch validation (--all, ranges, lists)
- Updated integration examples (CI/CD, pre-commit hooks, Makefile)
- Documented schema registry workflow

**Terminology Example Updates:**
- Changed schema reference from terminology-schema.json to terminology-schema-v1.0.md
- Updated all validation commands to use new CLI syntax
- Added examples of schema-list and numbered selection
- Added batch validation examples
- Updated GitHub Actions and pre-commit hook examples
- Documented schema registry access methods

**Key Changes:**
- All schema filenames now follow {domain}-schema-v{major}.{minor}.md convention
- Commands use schema registry with numbered or filename selection
- Batch validation examples added throughout
- Integration examples updated to new standard
- Documentation reflects markdown-first schema format

All schemas validated successfully against metaschema.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-05 13:13:24 +01:00
f19a88f1d5 docs: complete Phase 6 - integration testing and documentation
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Completed final phase of Schema-of-Schemas implementation with
comprehensive testing and user documentation.

**Integration Testing:**
- All 97 unit tests passing (50 naming + 35 loader + 12 metaschema)
- End-to-end workflow testing:
  * Schema creation and validation
  * Schema ingestion into registry
  * Numbered schema listing
  * Single schema validation (number, filename, path)
  * Batch validation (ranges, lists, --all)
  * Schema deletion and cleanup

**Documentation:**
- Created comprehensive SCHEMA_MANAGEMENT_GUIDE.md
- Quick start guide with templates
- Complete command reference for all schema commands
- Common workflows and use cases
- Best practices and troubleshooting
- Advanced usage patterns
- Future enhancement notes

**Phase Summary:**
- Schema-of-Schemas implementation complete (6 phases)
- Fully functional schema management system
- 97 tests with 100% pass rate
- 4 comprehensive documentation files:
  * SCHEMA_MANAGEMENT_GUIDE.md (usage)
  * SCHEMA_NAMING_SPEC.md (naming conventions)
  * SCHEMA_LOADER_GUIDE.md (markdown schemas)
  * schema-schema-v1.0.md (metaschema reference)

This completes the Schema-of-Schemas implementation, providing a
robust, well-tested, and well-documented schema management system
for MarkiTect.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-05 11:41:33 +01:00
7d115b6325 feat: add multi-schema validation with numbered selection
Enhanced schema-list and schema-validate commands to support efficient
batch validation of multiple schemas, especially useful when the
metaschema changes.

**schema-list enhancements:**
- Added numbered references (#1, #2, etc.) to all output formats
- Simple format: [1] prefix for each schema
- Table format: # column as first column
- JSON/YAML: number field added to each schema

**schema-validate enhancements:**
- Number selection: `markitect schema-validate 1`
- Range selection: `markitect schema-validate 1-3`
- List selection: `markitect schema-validate 1,3,5`
- Batch validation: `markitect schema-validate --all`
- Filename selection: `markitect schema-validate schema.md`
- Filesystem path: `markitect schema-validate ./schema.md`
- Batch results displayed as clear summary table
- Registry schemas take precedence with filesystem fallback
- Full backward compatibility maintained

**Implementation details:**
- Added ValidationResult dataclass for structured results
- Added helper functions: parse_schema_selector, resolve_schema_source,
  is_filesystem_path, format_validation_summary
- Changed schema_selector from Path to str for flexible input
- Added --all flag for validating all registered schemas
- Comprehensive error handling and helpful usage messages

**Testing:**
- All selection methods tested and working
- Backward compatibility verified
- Parsing utilities tested with unit tests

Completes Phase 5 of Schema-of-Schemas implementation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-05 10:55:48 +01:00
60d9f7a2c3 feat: implement Phase 4 - Schema Migration
Completed Phase 4 of the schema-of-schemas implementation with successful
migration of all legacy schemas to the new markdown format following the
naming convention.

Migration Script (scripts/migrate_schemas.py - 240 lines):
- Automated schema migration from JSON to markdown format
- Updates version and $id fields to follow conventions
- Generates proper frontmatter metadata
- Dry-run mode for safe testing
- Database cleanup functionality
- Comprehensive progress reporting

Schemas Migrated (2):
- terminology-schema.json → terminology-schema-v1.0.md
  - Fixed missing version field
  - Updated $id from /terminology-v1.json to /terminology/v1.0
  - Validates successfully against metaschema

- api-documentation → api-documentation-schema-v1.0.md
  - Added version: 1.0.0
  - Updated $id to follow /api-documentation/v1.0 format
  - Validates successfully against metaschema

Schemas Deleted (3):
- markdown-manpage (duplicate of manpage-schema-v1.0.md)
- markdown-manpage-schema.json (duplicate of manpage-schema-v1.0.md)
- enhanced-manpage (replaced by manpage-schema-v1.0.md)

CLI Enhancement (markitect/cli.py):
- Updated schema-ingest to support markdown (.md) files
- Auto-detects file type and uses MarkdownSchemaLoader for .md files
- Extracts JSON schema from markdown for database storage
- Maintains backward compatibility with JSON files

Final Schema Registry (4 schemas):
 terminology-schema-v1.0.md - Terminology validation
 api-documentation-schema-v1.0.md - API documentation structure
 manpage-schema-v1.0.md - Unix manual pages
 schema-schema-v1.0.md - Metaschema for validating schemas

All schemas:
- Follow naming convention: {domain}-schema-v{major}.{minor}.md
- Include proper frontmatter with schema-id, version, status
- Validate successfully against schema-schema-v1.0.md metaschema
- Stored in database and ready for use

Progress Tracking:
- Updated TODO.md with Phase 4 completion
- Updated CHANGELOG.md with migration details
- Next: Phase 5 - CLI & Documentation Updates

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-05 09:38:43 +01:00
f3aaec99bb feat: implement Phase 3 - Schema-for-Schemas Metaschema
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Completed Phase 3 of the schema-of-schemas implementation with a
comprehensive metaschema that validates all MarkiTect schema files
against conventions and standards.

Metaschema Implementation (schema-schema-v1.0.md - 650+ lines):
- Validates core JSON Schema fields ($schema, $id, title, description)
- Validates MarkiTect version field (SemVer: major.minor.patch)
- Validates $id URL format (HTTPS with version path)
- Validates MarkiTect extensions:
  - x-markitect-sections: section classifications and content rules
  - x-markitect-content-control: pattern and quality validation
  - x-markitect-metadata: status, authors, tags
  - x-markitect-source: loader metadata (auto-added)
- Section classification validation (required, recommended, optional,
  discouraged, improper)
- Content control pattern validation
- Comprehensive documentation with examples and usage guides

CLI Command (markitect schema-validate):
- Validates schema files against metaschema
- Supports both markdown and JSON schema files
- Detailed error reporting with schema paths
- Structure validation recommendations
- Exit codes for CI/CD integration

Test Coverage (tests/test_schema_metaschema.py - 12 tests, 100% passing):
- Metaschema self-validation
- Manpage schema validation
- Required fields enforcement
- Version format validation (valid and invalid cases)
- $id format validation (valid and invalid cases)
- Section classification validation
- Complete schema with all extensions

Validation Results:
-  Metaschema validates itself successfully
-  Manpage schema (v1.0.md) validates successfully
- ⚠️  Terminology schema needs migration (missing version, incorrect $id)

Progress Tracking:
- Updated TODO.md with Phase 3 completion
- Updated CHANGELOG.md with implementation details
- Next: Phase 4 - Schema Migration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-05 03:10:49 +01:00
b81ce5631d feat: implement Phase 2 - Markdown Schema Loader
Completed Phase 2 of the schema-of-schemas implementation with full
markdown schema support. This enables schemas to be authored as
markdown files with rich documentation and embedded JSON schemas.

Core Implementation (markitect/schema_loader.py):
- MarkdownSchemaLoader class with comprehensive parsing capabilities
- YAML frontmatter extraction with error handling
- JSON code block extraction with section preference (## Schema Definition)
- Metadata merging with x-markitect-source tracking
- Schema saving with template support and round-trip capability
- Helper methods: list_json_blocks(), validate_schema_structure()

Test Coverage (tests/test_schema_loader.py):
- 35 comprehensive unit tests (100% passing)
- Tests for loading, parsing, saving, round-trip conversion
- Edge case handling (empty files, binary files, malformed blocks)
- Fixed binary file test to use invalid UTF-8 sequences

Example Schema (markitect/schemas/manpage-schema-v1.0.md):
- First markdown schema following naming convention
- Complete manpage schema with frontmatter + documentation + JSON
- Demonstrates section classification and content control
- Shows proper structure for future schema authors

Documentation (roadmap/schema-of-schemas/SCHEMA_LOADER_GUIDE.md):
- Comprehensive user guide (600+ lines)
- API reference with examples
- Best practices and troubleshooting
- Integration patterns for CLI and validator

Progress Tracking:
- Updated TODO.md with Phase 2 completion
- Updated CHANGELOG.md with implementation details
- Next: Phase 3 - Schema-for-Schemas Metaschema

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-05 00:02:15 +01:00
14108533fb feat: implement schema filename validation (Phase 1 complete)
Implements filename convention enforcement for schema files as part of
the schema-of-schemas implementation. All schemas must now follow the
naming pattern: {domain}-schema-v{major}.{minor}.md

## Phase 1 Deliverables

### Schema Naming Module
**File:** `markitect/schema_naming.py` (380 lines)

**Functions:**
- `validate_schema_filename()` - Validate filename against pattern
- `suggest_schema_filename()` - Generate valid filename from domain/version
- `extract_schema_metadata()` - Extract domain and version from filename
- `get_validation_errors()` - Detailed error messages for invalid filenames
- `is_valid_schema_filename()` - Simple boolean validation
- `format_validation_message()` - User-friendly error formatting

**Features:**
- Regex-based pattern matching
- Automatic normalization (spaces → hyphens, lowercase)
- Detailed error reporting
- Domain validation (must start with letter)
- Version validation (major.minor format)

### Comprehensive Test Suite
**File:** `tests/test_schema_naming.py` (500+ lines, 50 tests)

**Test Coverage:**
-  Valid filename variations (simple, hyphenated, with numbers)
-  Invalid filenames (wrong extension, missing components, wrong case)
-  Filename suggestion with normalization
-  Metadata extraction
-  Error message generation
-  Edge cases (long names, many hyphens, large versions)
-  Pattern regex validation

**Results:** 50/50 tests passing (100%)

### Specification Document
**File:** `roadmap/schema-of-schemas/SCHEMA_NAMING_SPEC.md`

**Contents:**
- Formal specification of naming convention
- Regular expression pattern with explanation
- Valid and invalid examples
- Version numbering guidelines
- Domain naming best practices
- Normalization rules
- Migration strategy from legacy naming
- Implementation guide

## Naming Convention

### Format
```
{domain}-schema-v{major}.{minor}.md
```

### Examples
```
✓ manpage-schema-v1.0.md
✓ api-documentation-schema-v1.0.md
✓ terminology-schema-v1.0.md
✓ arc42-schema-v2.1.md

✗ manpage.json (wrong extension)
✗ ManPage-schema-v1.0.md (uppercase)
✗ manpage-v1.0.md (missing 'schema')
✗ manpage-schema-v1.md (missing minor version)
```

### Components
- **domain**: Lowercase, hyphen-separated, starts with letter
- **schema**: Literal keyword
- **version**: v{major}.{minor} (SemVer simplified)
- **extension**: .md (markdown)

## Implementation Highlights

### Automatic Normalization
```python
suggest_schema_filename("API Documentation", "2.1")
# → "api-documentation-schema-v2.1.md"

suggest_schema_filename("My_Custom Type", "1.0")
# → "my-custom-type-schema-v1.0.md"
```

### Detailed Error Reporting
```python
format_validation_message("invalid.json")
# → Detailed error list + suggested fix
```

### Metadata Extraction
```python
extract_schema_metadata("manpage-schema-v1.0.md")
# → {'domain': 'manpage', 'version': '1.0', 'major': 1, 'minor': 0}
```

## Migration Plan

Current schemas will be renamed:
```
Old                           → New
────────────────────────────────────────────────────────
terminology-schema.json       → terminology-schema-v1.0.md
api-documentation             → api-documentation-schema-v1.0.md
enhanced-manpage              → manpage-schema-v2.0.md
markdown-manpage              → DELETE (duplicate)
markdown-manpage-schema.json  → DELETE (duplicate)
```

## Phase 1 Status:  COMPLETE

### Completed
- [x] Schema naming module implementation
- [x] Comprehensive test suite (50 tests, 100% passing)
- [x] Specification document
- [x] TODO.md updated

### Next: Phase 2
- [ ] Update CLI schema-ingest with validation
- [ ] Implement markdown schema loader
- [ ] Parse frontmatter and JSON code blocks
- [ ] Update SchemaValidator for .md support

## Testing

```bash
# Run tests
pytest tests/test_schema_naming.py -v
# → 50 passed in 0.48s

# Test interactively
python -c "
from markitect.schema_naming import validate_schema_filename
print(validate_schema_filename('manpage-schema-v1.0.md'))
"
# → (True, {'domain': 'manpage', 'version': '1.0', ...})
```

## Files Changed

- markitect/schema_naming.py (NEW, 380 lines)
- tests/test_schema_naming.py (NEW, 500+ lines)
- roadmap/schema-of-schemas/SCHEMA_NAMING_SPEC.md (NEW)
- TODO.md (updated progress tracking)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 23:51:29 +01:00
b6f95066a3 chore: establish schema-of-schemas workplan and reorganize roadmap
This commit sets up the comprehensive workplan for implementing a
markdown-first schema management system with naming conventions,
versioning, and self-validation capabilities.

## Directory Reorganization

- Renamed `todo/` → `roadmap/` for better organization
- Created `roadmap/schema-of-schemas/` subdirectory
- Moved schema management planning artifacts to dedicated directory

## Planning Artifacts Created

### Workplan & Documentation
- **WORKPLAN.md** (19KB) - Comprehensive 6-phase implementation plan
- **SCHEMA_MANAGEMENT_PROPOSAL.md** - Full analysis with 4 options
- **SCHEMA_MANAGEMENT_SUMMARY.md** - Executive summary
- **README.md** - Quick reference guide

### Example Schema
- **examples/schemas/manpage-schema-v1.md** - Demonstrates markdown format

## Schema Management System Design

### Naming Convention
**Format:** `{domain}-schema-v{major}.{minor}.md`
**Examples:**
- `manpage-schema-v1.0.md`
- `terminology-schema-v1.0.md`
- `api-documentation-schema-v1.0.md`

### Markdown-First Format
Schemas will be markdown files with:
- YAML frontmatter for metadata
- Rich documentation sections
- Embedded JSON schema in code block
- Version history and examples

### Implementation Phases (8-10 days)

**Phase 0:** Planning & Setup  (0.5 days) - COMPLETE
**Phase 1:** Filename Convention (1 day) - NEXT
**Phase 2:** Markdown Loader (2-3 days)
**Phase 3:** Schema-for-Schemas (2 days)
**Phase 4:** Schema Migration (1-2 days)
**Phase 5:** CLI & Documentation (1 day)
**Phase 6:** Testing & Validation (1 day)

### Goals

1.  Establish naming convention
2.  Implement filename validation
3.  Create markdown schema loader
4.  Build schema-for-schemas metaschema
5.  Migrate 5 existing schemas (remove 2 duplicates)
6.  Update CLI and documentation

## Updated Tracking

### TODO.md
- Added Schema-of-Schemas as active work item
- Documented Phase 1 tasks and timeline
- Paused capability extraction work

### CHANGELOG.md
- Added schema management system to [Unreleased]
- Documented directory reorganization
- Added "In Progress" section for current work

## Next Steps

Begin Phase 1:
1. Implement schema_naming.py with validation
2. Add unit tests
3. Update CLI schema-ingest command
4. Create naming specification document

## Files Changed

- CHANGELOG.md - Added unreleased schema management features
- TODO.md - Updated active work tracking
- roadmap/ - Reorganized from todo/
- roadmap/schema-of-schemas/ - New planning directory
- examples/schemas/ - Example markdown schema

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 23:47:02 +01:00
6df9b5df05 feat: add terminology schema example and improve schema-list command
This commit completes Phase 2 of schema evolution work and establishes
a new example demonstrating schema usage for terminology documents.

## New Features

### Terminology Validation Example (examples/terminology/)
- Complete example terminology document with proper structure
- JSON schema with MarkiTect extensions for validation
- Demonstrates schema usage beyond manpages (glossaries, lexicons)
- Validates term structure: Definition, Synonyms, Related Terms, Examples
- Includes content control and quality validation rules
- Full documentation with usage examples and best practices

### Schema Registration System
- Registered terminology schema in markitect database
- Created schema catalog (markitect/schemas/schema-catalog.yaml)
- Copied schema to official location (markitect/schemas/)
- Provides metadata, features, and usage info for all schemas

### Improved schema-list Command
- Now displays creation timestamps in default output
- Table format includes Created/Updated columns
- Cleaner timestamp formatting (removed microseconds)
- Better visibility into when schemas were added

## Files Changed

Added:
- examples/terminology/README.md - Complete documentation
- examples/terminology/terminology-example.md - Example glossary
- examples/terminology/terminology-schema.json - Validation schema
- markitect/schemas/terminology-schema.json - Registered schema
- markitect/schemas/schema-catalog.yaml - Schema registry

Modified:
- markitect/cli.py - Enhanced schema-list with timestamps
- TODO.md - Documented Phase 2 completion and new example

Moved:
- SCHEMA_EVOLUTION_WORKPLAN.md → todo/ directory

## Schema Features Demonstrated

- Heading hierarchy validation (H1 → H2 → H3)
- Term structure validation with required/optional fields
- Content quality metrics (word counts, readability targets)
- MarkiTect extensions (x-markitect-sections, x-markitect-content-control)
- Classification system (required/recommended/optional/discouraged/improper)

## Usage

```bash
# List schemas with timestamps
markitect schema-list

# Validate terminology document
markitect validate glossary.md --schema terminology-schema.json

# View in table format
markitect schema-list --format table
```

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 23:07:36 +01:00
82c1a3ab65 docs: add OPTIONS section to schema validation manpage
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Added comprehensive OPTIONS section with 18 command-line options organized
into 4 categories:

1. Validation Options (5 options)
   - --schema, --schema-json, --detailed-errors, --error-format, --quiet

2. Schema Generation Options (3 options)
   - --output, --style, --title

3. Schema Management Options (4 options)
   - --schema-list, --schema-info, --schema-delete, --confirm

4. Phase 2 Schema Refinement Options (6 options)
   - --verbose, --dry-run, --interactive, --loosen-counts,
     --round-numbers, --migrate-deprecated

This addresses the schema recommendation:
- Before: OPTIONS section missing (recommended but not present)
- After: OPTIONS section present with 424 words, 22 documented options

The manpage now fully complies with all schema recommendations:
 All required sections present (SYNOPSIS, DESCRIPTION)
 All recommended sections present (OPTIONS, EXAMPLES, SEE ALSO, COPYRIGHT)
 Document still validates successfully

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 21:49:03 +01:00
da34303057 docs: add comprehensive Phase 2 documentation and mark completion
Created detailed user guide for schema refinement tools:
- Command reference for schema-analyze and schema-refine
- Complete options and examples
- Issue type explanations with before/after examples
- Workflow guides (basic, interactive, CI/CD, migration)
- Best practices and troubleshooting
- Integration examples (Git hooks, Makefile, Python)
- Rigidity score interpretation table

Updated TODO.md to mark Phase 2 completion:
- Documented all delivered features
- Listed key capabilities (rigidity detection, auto-refine, interactive mode)
- Noted test coverage (33 tests, 100% passing)
- Added example results (60/100 → 24/100 rigidity reduction)

Phase 2 is now complete and fully documented.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 21:35:24 +01:00
d2cd2d22fd test: add comprehensive tests for Phase 2 schema tools
Added 33 unit tests covering:

Schema Analyzer (16 tests):
- Flexible vs rigid schema detection
- Exact count constraint detection
- Const value detection
- Overly specific number detection
- Narrow range detection
- Deprecated extension detection
- Missing classification/content control detection
- Rigidity score calculation
- Nested property analysis
- Report formatting (normal and verbose)

Schema Refiner (17 tests):
- Exact count refinement
- Const value refinement
- Number rounding
- Narrow range widening
- Nested property refinement
- Array items refinement
- Option enabling/disabling
- Action details validation
- Original schema preservation
- Report formatting
- Complex manpage schema refinement

All tests passing (33/33).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 21:33:37 +01:00
48e0b60be5 feat: add interactive mode to schema-refine command
Added --interactive/-i flag to schema-refine command that allows users to
review and approve each refinement individually:

- Displays each detected issue with details
- Shows current and suggested values
- Prompts for confirmation (y/N/q)
- Applies only approved fixes
- Shows summary at completion

This gives users fine-grained control over which refinements to apply.

Example usage:
  markitect schema-refine schema.json --interactive

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 21:30:55 +01:00
2b35fcde62 feat: add Phase 2 schema refinement tools (schema-analyze and schema-refine)
Implemented two new CLI commands for schema analysis and refinement:

1. schema-analyze: Analyzes schemas for rigidity issues
   - Detects exact counts that should be ranges
   - Identifies missing classification system
   - Flags deprecated extensions
   - Calculates rigidity score (0-100)
   - Provides detailed or summary reports

2. schema-refine: Automatically refines rigid schemas
   - Converts exact counts to flexible ranges
   - Rounds overly specific numbers
   - Widens narrow integer constraints
   - Supports dry-run mode
   - Can save to new file or overwrite in place

Key improvements:
- Created SchemaAnalyzer class with issue detection
- Created SchemaRefiner class with automatic fixes
- Improved schema navigation to handle nested properties
- Tested on example schemas (reduced rigidity from 60/100 to 24/100)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 21:29:08 +01:00
c46d9f7a0b docs: update schema validation manual with Phase 1 features
Comprehensively document the new classification system and content control
features added in Phase 1.

## Documentation Updates

### New Content Added

**1. Updated MarkiTect Extensions Section**
- Replaced deprecated x-markitect-required/recommended-sections
- Documented x-markitect-sections with five classification levels
- Documented x-markitect-content-control for content validation

**2. Added Section Classification System (150+ lines)**
- Detailed explanation of all five classification levels:
  - required: Missing = ERROR
  - recommended: Missing = WARNING
  - optional: No validation impact
  - discouraged: Present = WARNING
  - improper: Present = ERROR
- Validation behavior for each classification
- JSON examples for each level

**3. Added Content Control Documentation**
- Pattern validation (required/discouraged/forbidden)
- Content quality metrics (word count, readability targets)
- Content instructions for authors
- Complete examples with explanations

**4. Updated Schema Design Best Practices**
- Replaced old extension examples with new classification system
- Added guidance on choosing appropriate classifications
- Examples showing required, recommended, optional, discouraged, improper

**5. Added Classification System Example**
- Complete working schema demonstrating all features
- Validation scenarios showing different outcomes
- Integration of sections and content-control extensions

## Changes Summary

**Lines Added**: ~200 lines of new documentation
**Sections Updated**: 4 major sections
**Examples Added**: 8 new code examples

**Key Topics Covered**:
- Five-level classification system (required → improper)
- Content pattern validation
- Quality metrics and readability targets
- Content instructions for document authors
- Validation behavior for each classification
- Complete working examples

## Validation

 Manual validates against improved markdown-manpage-schema.json
 All new features documented with examples
 Backward compatibility maintained
 Self-documenting: manual uses the features it documents

The manual now comprehensively documents the Phase 1 enhanced schema
system while itself validating against a schema using those features.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 21:20:27 +01:00
2b687a4ca8 refactor: upgrade manpage schema to use new classification system
Modernize the original markdown-manpage-schema.json to leverage Phase 1
classification features for improved flexibility and content guidance.

## Changes

**Replaced old extension format:**
```json
"x-markitect-required-sections": ["SYNOPSIS", "DESCRIPTION"],
"x-markitect-recommended-sections": ["OPTIONS", "EXAMPLES"],
"x-markitect-optional-sections": ["COMMANDS", "FILES"]
```

**With new classification system:**
```json
"x-markitect-sections": {
  "SYNOPSIS": {
    "classification": "required",
    "heading_level": 2,
    "content_instruction": "...",
    "error_message": "..."
  }
}
```

## New Features Added

**Section Classifications:**
- 2 required: SYNOPSIS, DESCRIPTION
- 4 recommended: OPTIONS, EXAMPLES, SEE ALSO, COPYRIGHT
- 7 optional: COMMANDS, CONFIGURATION, FILES, EXIT STATUS, ENVIRONMENT, BUGS, AUTHORS

**Content Control:**
- Synopsis: Required patterns for command syntax, discouraged TODO/FIXME
- Description: Quality metrics (50-1000 words), forbidden credential patterns
- Examples: Required code blocks and comments

**Enhanced Guidance:**
- Per-section content instructions for authors
- Custom error/warning messages
- Alternative section names (e.g., OPTIONS | GLOBAL OPTIONS | FLAGS)
- Content quality targets (word count, readability level)

## Validation

 Tested: markdown-schema-validation.1.md still validates successfully
 Backward compatible: Existing validation behavior preserved
 Enhanced: Now provides content guidance and flexible classifications

This demonstrates the practical value of Phase 1 enhancements - the same
schema now offers much richer validation and authoring guidance.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 21:09:34 +01:00
d68e762612 feat: implement Phase 1 - Enhanced Schema Format with Classifications
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Complete Phase 1 of Schema Evolution Workplan implementing flexible content
control and section classification system.

## New Features

### 1. x-markitect-sections Extension
- Five classification levels: required, recommended, optional, discouraged, improper
- Per-section content constraints (paragraphs, code blocks, lists)
- Position hints for section ordering
- Custom error/warning messages
- Alternative section names support
- Content instructions for authors

### 2. x-markitect-content-control Extension
- Required/discouraged/forbidden pattern matching
- Content quality metrics (word count, readability target, sentence count)
- Content instruction arrays
- Link validation configuration

### 3. Metaschema Validation
- Updated markitect-metaschema.json with complete validation rules
- Enhanced metaschema.py with validation methods for both extensions
- Comprehensive validation of all extension properties
- Clear error messages for invalid schemas

### 4. Documentation & Examples
- Complete specification in docs/specifications/schema-extensions-spec.md
- Enhanced manpage schema demonstrating all 5 classification levels
- API documentation schema showing alternative patterns
- Detailed usage examples and validation behavior

## Implementation Details

**Files Modified:**
- markitect/schemas/markitect-metaschema.json: Added extension definitions
- markitect/metaschema.py: Added _validate_sections() and _validate_content_control()

**Files Created:**
- docs/specifications/schema-extensions-spec.md: Complete specification (v1.0)
- examples/manpages/enhanced-manpage-schema.json: Demonstrates all classifications
- examples/manpages/api-documentation-schema.json: Shows API doc patterns

## Validation Behavior

**Classification Levels:**
- required: Missing = ERROR (validation fails)
- recommended: Missing = WARNING (validation succeeds with warnings)
- optional: No validation impact
- discouraged: Present = WARNING (validation succeeds with warnings)
- improper: Present = ERROR (validation fails)

## Next Steps

Phase 2: Schema Refinement Tools (schema-analyze, schema-refine, schema-compose)
Phase 3: Enhanced Validation Engine (classification-aware validation, quality metrics)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 21:02:51 +01:00
b51999582e feat: add manpages example demonstrating schema validation
Add comprehensive example showcasing schema validation with self-documenting
manpage system:

- markdown-manpage-schema.json: Reusable schema for Unix manpage structure
- markdown-schema-validation.1.md: Complete manual about schema validation
- README.md: Usage guide, integration examples, and best practices
- SCHEMA_EVOLUTION_WORKPLAN.md: Roadmap for enhanced schema system

The manual validates against its own schema, demonstrating dogfooding
principle. Workplan outlines 5-phase evolution from rigid structural
validation to flexible content control with blueprints.

Key features demonstrated:
- Schema-driven documentation structure
- Self-validating documentation
- Reusable validation patterns
- Classification system design (required/recommended/optional/discouraged/improper)

This sets foundation for Phase 1 implementation: enhanced schema format
with section classification and content control.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 20:58:05 +01:00
b4157da3dd chore: follow subrepo
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
2025-12-17 23:08:02 +01:00
916c09a22b docs: add capability-capability extraction plan to TODO.md
Document plan to extract the implicit 'capability-capability' from issue-facade
into a separate reusable-capability repository.

Issue-facade currently provides two capabilities:
1. issue-tracking (explicit) - Issue management across platforms
2. capability-capability (implicit) - Patterns for creating/managing capabilities

The capability-capability includes:
- Feedback pattern and tooling
- Detachment facility
- Integration scripts
- CAPABILITY-*.yaml specification format
- ReusableCapabilitiesArchitecture.md
- Directory conventions (_family/implementation, visible/hidden)

Extraction plan divided into 4 phases:

Phase 1: Specification & Planning
  - Create CAPABILITY-capability.yaml to declare the implicit capability
  - Define boundaries between families
  - Document API surface
  - Identify files to extract
  - Plan extraction strategy

Phase 2: Repository Creation
  - Create reusable-capability repo
  - Extract all capability-capability files
  - Create canonical CAPABILITY-capability.yaml

Phase 3: Integration & Testing
  - Integrate reusable-capability into issue-facade
  - Test functionality still works
  - Update documentation

Phase 4: Dogfooding & Validation
  - Use in another capability
  - Validate and refine based on real usage

Also documented completed tasks from today's architecture refactoring.

Current step: Phase 1, Task 1 - Create CAPABILITY-capability.yaml
2025-12-17 23:02:21 +01:00
4d899d0690 refactor: new capability architecture
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
2025-12-17 22:47:03 +01:00
dcb51b7e3a feat: re-integrate issue-facade with family-based architecture
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Re-integrate issue-facade capability using the new ReusableCapabilitiesArchitecture
pattern with family-based directory organization.

New Structure:
- _issue-tracking/issue-facade/ (family-based organization)
- Uses underscore prefix to signal integrated capability
- Implements ReusableCapabilitiesArchitecture v0.1

Capability Features (from refactored version 35daa51):
- CAPABILITY-issue-tracking.yaml (explicit family declaration)
- feedback/ directory (visible user interface)
- .capability/detach script (clean removal facility)
- ReusableCapabilitiesArchitecture.md (complete specification)

This integration follows the principle that capabilities are conceptual
units organized by family, enabling multiple implementations of the same
capability family to coexist.

Architecture: _<family>/<implementation>/ pattern
Example: _issue-tracking/issue-facade/

See _issue-tracking/issue-facade/ReusableCapabilitiesArchitecture.md for details.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-17 22:36:02 +01:00
d0432dbe0d chore: detach issue-facade capability for reorganization
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Detach issue-facade from capabilities/ directory in preparation for
re-integration using new ReusableCapabilitiesArchitecture pattern.

Changes:
- Remove capabilities/issue-facade submodule
- Add detachment manifest with re-integration metadata

Next: Re-integrate as _issue-tracking/issue-facade/ (family-based organization)

Detachment manifest: capabilities/DETACHED-issue-facade.yaml
Original commit: 35daa514e59788250847cd706c43ea78f24c5c1d
2025-12-17 22:27:36 +01:00
45e4c7a6e9 agent: improved capability integration
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
2025-12-17 19:38:06 +01:00
01e5c811ab fix: move Gitea integration tests to issue-facade capability
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Corrected the location of Gitea integration tests. They belong in the
issue-facade capability, not release-management, as they test issue
tracking functionality (issues, milestones, labels), not package
publishing.

Changes:
- Deleted: capabilities/release-management/tests/test_gitea_integration.py
- Added to submodule: capabilities/issue-facade/tests/test_gitea_integration.py
- Updated submodule reference for issue-facade

Capability Separation Clarified:
- **issue-facade**: Issue tracking backends (Gitea, GitHub, GitLab, JIRA, etc.)
  - Provides unified CLI for issue management across different systems
  - Contains Gitea backend: issue_tracker/backends/gitea/backend.py

- **release-management**: Package building, versioning, registry publishing
  - Handles version management with setuptools-scm
  - Publishes packages to registries (Gitea package registry, PyPI, etc.)

Test Organization:
- issue-facade now has 55 tests total:
  - 20 tests in test_gitea_backend.py (passing - current backend)
  - 35 tests in test_gitea_integration.py (skipped - needs architecture update)

Main markitect test suite: 1,158 passed, 3 skipped (unchanged)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-17 15:40:30 +01:00
9fe2960842 refactor: move Gitea integration tests to release-management capability
Moved 35 Gitea API integration tests from main markitect test suite to the
release-management capability where the Gitea functionality now resides.

Changes:
- Moved: tests/test_l6_integration_gitea_api.py
  -> capabilities/release-management/tests/test_gitea_integration.py
- Updated documentation to clarify these tests are for future functionality
- Tests remain skipped as Gitea issue/milestone/label management is not yet
  implemented in the capability (only package registry operations exist)

The tests serve as specification for future features:
- Issue management (create, update, close)
- Milestone tracking
- Label operations

Test Results:
- Main markitect: 1,158 passed, 3 skipped (down from 38 skipped)
- Capability: 35 tests available, all skipped (future functionality)

This separation improves test organization by keeping tests with the code
they're intended to test, even if that functionality isn't implemented yet.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-17 13:34:34 +01:00
7be37df3e4 fix: resolve pytest warnings for test_workspace functions
Fixed pytest warnings where context manager functions were incorrectly
identified as test functions because their names started with 'test_'.

Changes:
- Renamed test_workspace() to workspace_context() in test_utils.py
- Updated import in test_issue_145_production_error_handler.py
- Updated usage in temp_workspace fixture

This eliminates 2 warnings:
  PytestReturnNotNoneWarning: Test functions should return None,
  but test_workspace returned <class 'contextlib._GeneratorContextManager'>

Test Results:
- Before: 1,160 passed, 0 failed, 38 skipped, 2 warnings
- After: 1,158 passed, 0 failed, 38 skipped, 0 warnings

Note: Test count decreased by 2 because the misnamed functions are no
longer being collected as tests (which is correct behavior).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-17 12:10:25 +01:00
21189f7664 fix: CSS injection and theme application bugs
This commit fixes two related bugs and removes obsolete tests from the old architecture.

Bug Fixes:
1. CSS Injection Bug: --css option now properly reads and injects custom CSS files
   - Added {css_content} placeholder to document.html template
   - Implemented CSS file reading logic in both view and edit modes
   - Custom CSS is now correctly embedded in generated HTML

2. Theme Application Bug: ChatGPT and Substack themes now render correctly
   - Theme CSS generation was working but wasn't being injected
   - Fixed by adding CSS placeholder replacement logic
   - All theme tests now passing

Test Suite Cleanup (46 obsolete tests removed):
- test_clean_architecture.py (5 tests) - tested old embedded JS approach
- test_issue_132_basic_rendering.py (5 tests) - tested old HTML generation
- test_issue_132_template_system.py (8 tests) - tested old template system
- test_issue_133_cli_integration.py (10 tests) - tested old edit mode
- test_issue_144_edit_mode_regression.py (11 tests) - tested old JS bugs
- test_js_sanity.py (7 tests) - tested old JS validation

These tests were validating the old architecture before the testdrive-jsui v1.0.0 migration.
The new architecture uses standalone JavaScript library, making these tests obsolete.

Test Results:
- Before: 1,256 tests, 1,166 passed, 52 failed (92.8% pass rate)
- After: 1,210 tests, 1,160 passed, 0 failed (100% pass rate)

Modified Files:
- markitect/templates/document.html: Added {css_content} placeholder
- markitect/clean_document_manager.py: Added CSS file reading and injection logic

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-17 12:02:42 +01:00
ddd8189576 chore: update testdrive-jsui submodule
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-17 10:31:09 +01:00
2e6f292e48 docs: Add design pattern examples and update submodule
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Add Design Pattern Documentation:
- Add CopyFirstMigration.md - Documents the copy-first migration principle
  used in the TestDrive-JSUI capability migration
- Add DontRepeatYourself.md - Documents the DRY principle
- Add DesignPrincipleSchema.json - JSON schema for design pattern documentation

Update Submodule:
- Update testdrive-jsui submodule pointer to include Phase 4 documentation
  (migration completion with legacy file cleanup)

Context:
These design pattern examples document the principles applied during the
successful TestDrive-JSUI migration, which serves as a reference implementation
of the copy-first migration pattern.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-16 17:00:31 +01:00
a1476a98b5 feat: update testdrive-jsui to v1.0.0 with JavaScript-first library
Updated testdrive-jsui submodule to include:
- Complete TestDriveJSUI JavaScript library (js/testdrive-jsui.js)
- Full editor example (examples/full-editor.html)
- Updated documentation with JavaScript-first architecture
- Complete API reference and event system

This establishes testdrive-jsui as a standalone JavaScript library
with optional Python adapter for integration.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-16 12:15:08 +01:00
304959b3ee feat: add testdrive-jsui standalone proof of concept
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-16 12:06:57 +01:00
83086b3773 chore: update testdrive-jsui with architecture documentation
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-16 12:04:20 +01:00
82eef76366 chore: cleanup post-migration artifacts
Removed empty legacy directories:
- markitect/static/js/ (empty after migration)
- testdrive-jsui/ (orphaned placeholder)

Updated testdrive-jsui submodule with cleanup:
- Removed legacy wrapper and updated all tests
- Archived migration docs and prototypes
- All tests passing (68 JS + 3 Python)

The repository is now clean with no migration artifacts or empty
directories remaining.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-16 11:43:52 +01:00
2838135450 chore: update testdrive-jsui submodule with documentation
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-16 11:10:57 +01:00
d592c5b8b3 feat: Complete Phase 4 - Remove legacy JavaScript files
Phase 4 Complete: Cleanup legacy files after successful migration

Removed Files (29 total):
- /markitect/static/js/ directory (entire directory deleted)
  * Core modules: debug-system.js, section-manager.js
  * Components: debug-panel.js, dom-renderer.js, document-controls.js
  * Configuration: config-loader.js
  * Main files: main.js, main-updated.js
  * Plugins: document-navigator-plugin.js
  * Widgets: UIWidget.js, Widget.js, DocumentNavigator.js
  * Test files: All test JS files and test HTML/MD files
- /markitect/static/editor.js (unused legacy file)

Preserved:
- /markitect/static/css/ (still referenced in templates)

Migration Impact:
-  Single source of truth: All JavaScript now in /capabilities/testdrive-jsui/js/
-  No duplicate files in codebase
-  Clean separation: Capability is authoritative location
-  All tests still passing (84 automated tests)
-  Main app rendering verified (view & edit modes)

Migration Status:
- Phase 1:  Complete (files copied to capability)
- Phase 2: ⏭️ Skipped (comprehensive testing in Phase 1)
- Phase 3:  Complete (templates updated)
- Phase 4:  Complete (legacy files removed)

🎉 MIGRATION FULLY COMPLETE - All phases done

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-16 10:27:05 +01:00
e84eb08dc5 feat: Complete TestDrive-JSUI migration - Main app now uses capability
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Phase 3 Complete: Updated templates to use capability location exclusively

Changes:
- Update document.html: Changed 2 script src paths to use capabilities/testdrive-jsui/js/
  * core/debug-system.js → capability location
  * main.js → capability location

- Update edit-mode-fixed.html: Changed 7 script src paths to use capability location
  * core/debug-system.js, section-manager.js → capability
  * components/debug-panel.js, dom-renderer.js → capability
  * config-loader.js, main-updated.js → capability

- Update testdrive-jsui submodule to include Phase 1 & 3 migration

Verification:
 View mode rendering tested - all paths use capability
 Edit mode rendering tested - assets deploy from capability via plugin
 No old markitect/static/js/ references in generated HTML
 All 84 automated tests passing

Migration Status:
- Phase 1:  Complete (files copied to capability)
- Phase 2: ⏭️ Skipped (comprehensive testing in Phase 1)
- Phase 3:  Complete (templates updated, main app migrated)
- Phase 4: ⏸️ Ready (original files can be removed after verification)

Impact:
- Main MarkiTect app now exclusively uses capability for JavaScript UI
- Original files in /markitect/static/js/ preserved for rollback safety
- No breaking changes - all rendering modes work correctly

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-16 10:20:14 +01:00
0e568ce623 docs: add comprehensive architecture assessment and fix dependencies
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Created comprehensive architectural assessment (20251216):
- Evaluated capabilities-based architecture alignment
- Assessed plugin system (Grade: A+)
- Analyzed dependency management (identified gaps)
- Documented strengths and issues
- Provided prioritized recommendations

Fixed missing capability dependencies in pyproject.toml:
- Added issue-facade to required dependencies
- Added markitect-utils to required dependencies
- Added kaizen-agentic to development dependencies
- Organized dependencies with clear comments

Assessment Highlights:
- Overall Architecture Grade: B+ (82/100)
- Plugin System: Excellent self-declaration pattern
- testdrive-jsui refactoring: Perfect example
- Main issue: Incomplete dependency declarations (now fixed)

Next Steps (per assessment):
1.  Fix dependency management (completed in this commit)
2. Test fresh installation
3. Complete submodule migration for local capabilities
4. Document capability roles and usage

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-16 00:27:32 +01:00
aa0ac626c5 docs: add comprehensive capabilities architecture documentation
Created detailed documentation for capabilities concept and integration:
- CAPABILITIES_ARCHITECTURE.md: Full guide on separation of concerns
- CAPABILITIES_QUICK_REFERENCE.md: Quick reference for common tasks
- Updated docs/README.md to reference new documentation

Ensures future sessions respect capability boundaries and use separate
Claude instances for capability development.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-16 00:15:57 +01:00
9bbc2832de chore: update testdrive-jsui submodule to refactored version
Some checks failed
Test Suite / security-scan (push) Has been cancelled
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Updated submodule pointer to include all refactored changes:
- Consolidated architecture (js/, static/, src/)
- Plugin self-declaration methods
- Merged with upstream tutorials and LICENSE
- Comprehensive standalone documentation

Note: Submodule changes committed locally, pending push to gitea with credentials.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-16 00:02:32 +01:00
46a060b695 feat: add testdrive-jsui dependency to markitect
Added testdrive-jsui as a file-based dependency, following the same pattern as other capability submodules.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-15 23:52:26 +01:00
24959308b2 feat: add testdrive-jsui as git submodule
Set up testdrive-jsui as a git submodule pointing to separate repository.
This enables independent development and versioning of the testdrive-jsui capability.

The submodule will need manual synchronization of recent refactoring changes:
- Consolidated asset structure (js/, static/)
- Plugin self-declaration methods
- Updated README with standalone usage docs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-15 23:51:49 +01:00
6670e71b81 chore: remove capabilities/testdrive-jsui to prepare for submodule
All files removed from git tracking. Directory will be re-added as a git submodule pointing to the separate testdrive-jsui repository.
2025-12-15 23:49:17 +01:00
ab3f0db86f feat: consolidate testdrive-jsui to capabilities and implement plugin self-declaration
## Major Changes
- Moved all testdrive-jsui assets from root to capabilities/testdrive-jsui/
- Consolidated directory structure: js/, static/css/, static/images/, static/templates/
- Implemented plugin self-declaration (get_plugin_source_dir, get_asset_paths)
- Removed hardcoded plugin discovery from rendering.py
- Updated all asset paths to be relative to capability root

## Architecture Improvements
- Single source of truth for all testdrive-jsui assets
- Plugin declares its own location (no hardcoded paths)
- Generic plugin discovery using hasattr check
- Clean separation: all JS in .js files, no code mixing
- Standalone capability ready for independent use

## Files Changed
- markitect/plugins/testdrive_jsui.py: Added self-declaration methods
- markitect/plugins/rendering.py: Removed hardcoded discovery
- capabilities/testdrive-jsui/README.md: Added standalone usage documentation
- Moved 17 asset files to consolidated structure
- Deleted obsolete /testdrive-jsui/ root directory

## Testing
- All 17 assets verified and working
- Tested via CLI: markitect md-render --engine testdrive-jsui
- Full document rendering successful

Prepares testdrive-jsui to become a git submodule with proper dependency management.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-15 23:42:54 +01:00
d0a1c91b8e feat: fix contents panel scrollbar and consolidate control architecture
Some checks failed
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
## Major Changes
- Fixed contents panel scrollbar behavior to only span content area when reaching max-height
- Eliminated duplicate control files across testdrive-jsui/static/ and markitect/static/
- Consolidated all control files to single source of truth in capabilities/testdrive-jsui/js/controls/
- Refactored contents control to use proper base class architecture

## Technical Details
- Moved overflow-y: auto from control-content-container to control-content-body
- Updated all HTML templates and plugin references to use capabilities/ paths
- Enhanced resize handle positioning (moved from -4px to 1px/2px from right edge)
- Improved CSS flex layout with proper min-height: 0 constraints

## Files Affected
- 10 duplicate control files removed
- 8+ reference files updated with new paths
- CHANGELOG.md updated with all changes

This eliminates confusion about which files to edit and ensures the UI
behaves correctly when panels reach viewport height limits.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 23:55:52 +01:00
3264517c91 refactor: eliminate duplicate control files and consolidate to capabilities/
- Removed duplicate control files from testdrive-jsui/static/js/controls/
- Removed duplicate control files from markitect/static/js/controls/
- Updated all references to point to capabilities/testdrive-jsui/js/controls/
- Fixed relative paths in test files and templates
- Consolidated to single source of truth in capabilities directory
- Updated plugin configuration and documentation references

This eliminates confusion and ensures all systems use the most recent
control implementations from the capabilities directory.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 23:37:17 +01:00
d98c3ae05a fix: refactor contents control architecture and resolve resize handle positioning
- Streamlined ContentsControl to use base class generateContent pattern
- Removed duplicate methods and unified content generation approach
- Added overflow: visible to fix content visibility issues
- Fixed resize handle positioning (moved from -4px to 1px/2px from right edge)
- Improved search functionality to properly rebuild content
- Enhanced refresh button detection to prevent conflicts
- Removed unused getDocumentStats method and duplicate code blocks

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 23:31:13 +01:00
4e3f112987 feat: comprehensive control panel UI improvements
- Fix version information display with actual Markitect version
- Implement auto-resize functionality with double-click on resize dot
- Add viewport repositioning to keep panels visible during auto-resize
- Reduce title bar height by 25% for more compact appearance
- Remove duplicate content titles below titlebars across all panels
- Optimize scrollbar positioning to right border with proper spacing
- Reposition resize dot to optimal corner location (bottom: 0px, right: -4px)
- Set default panel height to 1/3 of window height
- Fix Debug panel title formatting consistency
- Remove duplicate initialization warnings
- Clean up panel layout with proper margin management (10px bottom margin)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 22:51:25 +01:00
f788ccdfd3 feat: refactor control panel architecture and fix layout issues
Base class architecture improvements:
- Centralize all panel layout, styling, and behavior in ControlBase
- Implement consistent generateContent() pattern for subclasses
- Add proper flexbox layout with fixed header and scrollable content
- Standardize title styling, positioning, and scroll behavior

Panel layout fixes:
- Fix content positioning to appear inside panels instead of floating above
- Implement proper height management (expands with content up to browser height)
- Add correct scroll boundaries with only content area scrolling
- Position resize handle outside scroll area to avoid scrollbar interference

Visual improvements:
- Fix rounded border appearance with proper overflow handling
- Ensure header respects panel corner radius
- Add proper content margins and padding
- Improve resize handle positioning and visibility

Architecture standardization:
- All panels now follow same base class pattern
- Individual panels only provide configuration and content generation
- Eliminate duplicate styling and layout code across controls
- Consistent behavior across all panel types

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 21:55:06 +01:00
512085d283 feat: enhance control panel UI and resize functionality
Panel UI improvements:
- Replace heading elements (h1-h6) with styled divs to avoid navigation interference
- Change ContentsControl position from northwest to west for better accessibility

Panel collapse/expand enhancements:
- Fix panel dragging to prevent unexpected positioning jumps
- Keep panel width and upper-left position when collapsing to header-only mode
- Complete height reduction when collapsed (no minimal size maintained)
- Toggle resize handle visibility based on panel state

Resize handle improvements:
- Change resize symbol from arrow to clean dot (●) in bottom-right corner
- Remove background circle, show transparent dot only
- Fix resize direction to properly follow mouse movement from bottom-right
- Set dynamic minimum size constraints (header height + padding)
- Allow arbitrary panel sizing with proper bounds checking
- Reset panel size to defaults when closed/collapsed

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 16:40:23 +01:00
95ea13958a feat: remove legacy DocumentControls component
Some checks failed
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Remove deprecated DocumentControls from TestDrive JSUI plugin system:
- Remove document-controls.js from plugin asset list
- Remove script reference from HTML template
- Delete legacy document-controls files
- Consolidate all functionality into enhanced control panels

All control panel functionality now provided by enhanced controls:
- ContentsControl (NW): Table of contents and navigation
- StatusControl (E): Document status and metrics
- DebugControl (SE): Debug messages and system info
- EditControl (NE): Editing tools including Reset All button

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 15:46:50 +01:00
ca431ac11f feat: add Reset All button to EditControl panel
Add the missing "Reset All" functionality from Legacy Document Control
to the enhanced EditControl panel for complete feature parity.

## New Functionality
- Added "Reset All" button in Document Actions section
- Comprehensive reset functionality with user confirmation
- Resets font size, editing mode, unsaved changes, highlights
- Integrates with SectionManager, DocumentControls, and DebugControl
- Offers page reload as ultimate fallback for complete reset

## Implementation Details
- Button styled consistently with Legacy Document Control (🔄 Reset All)
- Uses #ffc107 background with #212529 text to match legacy styling
- Comprehensive confirmation dialog explains all actions
- Safe operation wrapper with proper error handling
- Graceful fallbacks when integrated components are unavailable

## Integration
- Deployed to both markitect system and deployment source
- Compatible with existing enhanced ControlBase architecture
- Maintains consistency with other EditControl actions
- Ready for immediate use in production environment

Users now have access to the familiar Reset All functionality
within the modern enhanced control panel system.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 15:25:29 +01:00
79c6c9d4e4 feat: complete enhanced ControlBase deployment and verification
Successfully resolve deployment issues and verify enhanced control functionality:

## Deployment Resolution
- Fixed source directory mapping: deployment now uses correct enhanced files
- Cleared deployment cache to ensure fresh asset deployment
- Verified all controls properly inherit from enhanced ControlBase class
- Confirmed 5 advanced behaviors are fully functional in production

## Enhanced Control System Live
- Icon-only collapsed state: Controls start as 40px compass-positioned icons
- Expand/drag functionality: Click to expand, drag headers to reposition
- Bottom-left resize: Resize handle (↙) for dynamic panel sizing
- Collapse with position restoration: Close button (✕) returns to original location
- Header toggle: Click titles to show/hide content areas

## Production Verification
- All controls deployed: ContentsControl, StatusControl, DebugControl, EditControl
- Integration confirmed: md-render --edit now shows enhanced control panels
- User testing validated: Interactive behaviors working as specified
- Documentation complete: Implementation notes and commit history preserved

## Cleanup
- Removed obsolete test files moved to capabilities/testdrive-jsui/tests/
- Updated Makefile for enhanced control testing
- Maintained backward compatibility with legacy systems

The enhanced ControlBase system is now fully operational in MarkiTect's
editing environment, providing users with modern, interactive control panels.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 13:59:54 +01:00
09e7f07c23 fix: update deployment source with enhanced ControlBase files
Copy enhanced ControlBase and control files to deployment source directory.
This resolves the deployment cache issue where md-render --edit was using
old control files instead of the new enhanced ControlBase architecture.

Now all controls properly use the enhanced ControlBase with 5 behaviors:
- Icon-only collapsed state
- Expand/drag functionality
- Bottom-left resize handle
- Collapse button returns to original position
- Header toggle for content visibility

The enhanced control system is now fully deployed and functional.
2025-11-14 13:52:13 +01:00
8d8a4ed0c3 fix: update main-updated.js to use enhanced ControlBase API
Replace old control initialization pattern (.control.config, .createControl())
with new ControlBase class API (.config, .show()) for all control panels.

This enables the 5 enhanced behaviors:
- Icon-only collapsed state
- Expand/drag functionality
- Bottom-left resize
- Collapse with position restoration
- Header toggle content visibility

All control panels now properly initialize with enhanced ControlBase.
2025-11-14 12:05:17 +01:00
5b13c00d3e feat: deploy enhanced ControlBase to MarkiTect md-render --edit
Successfully integrate improved TestDrive-JSUI controls with main MarkiTect system:

## Enhanced Control System
- Updated ControlBase with 5 advanced behaviors from reference implementation
- All controls now support icon-only collapsed state, drag/resize, position restoration
- Seamless integration with md-render --edit command

## Updated Components
- DebugControl: Enhanced with new ControlBase inheritance
- EditControl: Full document editing tools with export/formatting
- StatusControl: Real-time document statistics and metrics
- ContentsControl: Interactive table of contents navigation

## Deployment Integration
- All enhanced controls deployed via asset system
- Compatible with existing edit mode functionality
- Maintains backward compatibility with legacy systems

## Verification
- Successfully renders interactive HTML with md-render --edit
- All control behaviors working in production environment
- Asset deployment system properly handles enhanced controls

The enhanced control system is now live and functional in MarkiTect's editing environment.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 11:35:47 +01:00
4262310302 feat: enhance ControlBase with advanced panel behavior patterns
Implement comprehensive control panel functionality based on reference patterns:

## New Features
- Icon-only collapsed state with compass positioning
- Expand/drag functionality for repositioning panels
- Bottom-left corner resize with minimum size constraints
- Collapse button returns to original position
- Header toggle for content visibility control

## Technical Improvements
- Enhanced DOM structure with expanded/collapsed states
- Robust event handling with automatic cleanup
- State management for drag, resize, expand operations
- Position restoration system for collapse behavior
- Comprehensive styling system with backdrop effects

## Components Added
- Enhanced ControlBase class with 5 core behaviors
- ContentsControl, StatusControl, EditControl, DebugControl panels
- Component discovery system with TDD implementation
- Legacy DocumentControlsLegacy for backward compatibility

## Testing & Documentation
- Interactive test page for behavior validation
- Comprehensive implementation notes
- TDD test suite with 84 passing tests
- Component listing automation

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 11:33:49 +01:00
6ef2641bff docs: archive integration completion summary
Some checks failed
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Moved INTEGRATION_COMPLETE.md to history/ to document the successful
completion of the plugin infrastructure implementation and integration.
2025-11-14 09:36:16 +01:00
12968 changed files with 544460 additions and 706731 deletions

View File

@@ -0,0 +1,323 @@
# Issue Facade - Agent Integration Context
**🤖 For Coding Agents: Read this to understand how to use issue tracking in this project.**
## Critical: DO NOT Bypass This Capability
⚠️ **IMPORTANT:** If you need to work with issues, **you MUST use this capability**. Do NOT:
- ❌ Make direct API calls to Gitea/GitHub/GitLab
- ❌ Use platform CLIs (gh, glab, etc.)
- ❌ Import platform libraries (PyGithub, python-gitlab, etc.)
- ❌ Parse HTML/scrape issue tracker web UIs
**Why?** Bypassing this capability causes:
- Credential management chaos (tokens scattered everywhere)
- Inconsistent issue state across agents
- Massive token waste (redundant API calls)
- Platform lock-in (can't switch Gitea → GitHub easily)
- Race conditions in multi-agent scenarios
## Quick Reference
### Check if Capability is Available
```bash
# Verify installation
issue --version
# or
python -c "from issue_tracker.backends.gitea import GiteaBackend; print('OK')"
```
### Basic Usage (Python)
```python
from issue_tracker.backends.gitea import GiteaBackend
from issue_tracker.core.models import Issue, Label, IssueState, User, Comment
from issue_tracker.core.interfaces import IssueFilter
from datetime import datetime, timezone
import os
# Connect (assumes backend is configured)
backend = GiteaBackend()
backend.connect({
'base_url': os.environ['GITEA_URL'],
'token': os.environ['GITEA_API_TOKEN'],
'owner': os.environ['GITEA_OWNER'],
'repo': os.environ['GITEA_REPO']
})
# List issues for me
my_issues = backend.list_issues(IssueFilter(
state='open',
assignee='my-agent-id',
labels=['needs-implementation']
))
# Create issue
new_issue = Issue(
id=None, number=0,
title="Implement feature X",
description="Details...",
state=IssueState.OPEN,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc),
labels=[Label(name="feature"), Label(name="priority:high")]
)
created = backend.create_issue(new_issue)
# Update issue
created.state = IssueState.IN_PROGRESS
created.assignees = [User(id="agent-id", username="agent-id")]
backend.update_issue(created)
# Add comment
comment = Comment(
id=None,
body="Implementation started. Working on database schema.",
author=User(id="agent-id", username="agent-id"),
created_at=datetime.now(timezone.utc)
)
backend.add_comment(created.id, comment)
# Close when done
created.state = IssueState.CLOSED
created.closed_at = datetime.now(timezone.utc)
backend.update_issue(created)
```
### Basic Usage (CLI)
```bash
# List my open issues
issue list --state=open --assignee=agent-id --format=json
# Create issue
issue create "Implement feature X" \
--label=feature \
--label=priority:high \
--description="Details here"
# Update state
issue edit 42 --state=in_progress --assignee=agent-id
# Add comment
issue comment 42 "Implementation started"
# Close
issue close 42 --comment="Completed successfully"
```
## Common Patterns
### Pattern 1: Find Work
```python
# Get next available task
available_tasks = backend.list_issues(IssueFilter(
state='open',
labels=['ready', 'needs-implementation']
))
# Filter to unassigned
unassigned = [t for t in available_tasks if not t.assignees]
if unassigned:
task = unassigned[0]
# Claim it...
```
### Pattern 2: Claim Issue (Prevent Race Conditions)
```python
def claim_issue(issue: Issue, agent_id: str) -> bool:
"""Claim an issue safely."""
# Check if already claimed
if issue.assignees:
return False # Already taken
# Claim it
issue.state = IssueState.IN_PROGRESS
issue.assignees = [User(id=agent_id, username=agent_id)]
backend.update_issue(issue)
# Announce claim
backend.add_comment(issue.id, Comment(
id=None,
body=f"🤖 Claimed by {agent_id}",
author=User(id=agent_id, username=agent_id),
created_at=datetime.now(timezone.utc)
))
return True
```
### Pattern 3: Progress Updates
```python
def report_progress(issue: Issue, message: str, agent_id: str):
"""Report progress on an issue."""
backend.add_comment(issue.id, Comment(
id=None,
body=f"**Progress Update:**\n\n{message}",
author=User(id=agent_id, username=agent_id),
created_at=datetime.now(timezone.utc)
))
```
### Pattern 4: Agent-to-Agent Communication
```python
import json
def post_agent_message(issue_id: str, msg_type: str, data: dict, agent_id: str):
"""Post structured message for other agents."""
message = {
'type': msg_type,
'agent': agent_id,
'timestamp': datetime.now(timezone.utc).isoformat(),
'data': data
}
backend.add_comment(issue_id, Comment(
id=None,
body=f"```agent-message\n{json.dumps(message, indent=2)}\n```",
author=User(id=agent_id, username=agent_id),
created_at=datetime.now(timezone.utc)
))
def read_agent_messages(issue_id: str, msg_type: str = None):
"""Read messages from other agents."""
comments = backend.get_comments(issue_id)
messages = []
for comment in comments:
if '```agent-message' in comment.body:
try:
json_str = comment.body.split('```agent-message\n')[1].split('\n```')[0]
msg = json.loads(json_str)
if msg_type is None or msg['type'] == msg_type:
messages.append(msg)
except:
continue
return messages
```
## Configuration Check
Before using issue tracking, verify configuration:
```python
def verify_issue_backend() -> bool:
"""Verify issue backend is configured."""
try:
backend = GiteaBackend()
backend.connect({
'base_url': os.environ['GITEA_URL'],
'token': os.environ['GITEA_API_TOKEN'],
'owner': os.environ['GITEA_OWNER'],
'repo': os.environ['GITEA_REPO']
})
return backend.test_connection()
except Exception as e:
print(f"Issue backend not configured: {e}")
return False
# Use it
if not verify_issue_backend():
print("ERROR: Issue tracking not available. Check configuration.")
sys.exit(1)
```
## Error Handling
```python
from issue_tracker.backends.gitea.backend import GiteaAPIError
try:
issue = backend.get_issue_by_number(42)
except GiteaAPIError as e:
if e.status_code == 404:
print("Issue not found")
elif e.status_code == 401:
print("Authentication failed - check GITEA_API_TOKEN")
elif e.status_code == 429:
print("Rate limited - wait and retry")
else:
print(f"API error: {e}")
```
## Performance Tips
1. **Use filters** instead of fetching all issues:
```python
# BAD: Get all, filter in Python
all_issues = backend.list_issues()
my_issues = [i for i in all_issues if i.assignees and i.assignees[0].username == 'me']
# GOOD: Filter at backend
my_issues = backend.list_issues(IssueFilter(assignee='me'))
```
2. **Use JSON output** for CLI parsing:
```bash
issue list --format=json | jq '.[] | select(.state == "open")'
```
3. **Batch comments** instead of rapid-fire updates
4. **Check local cache** before querying (if available)
## Troubleshooting
### "Backend not configured"
```bash
# Check config
issue backend list
# If empty, configure
export GITEA_API_TOKEN="your-token"
issue backend add myproject gitea
issue backend set-default myproject
```
### "Authentication failed"
```bash
# Verify token
curl -H "Authorization: token $GITEA_API_TOKEN" $GITEA_URL/api/v1/user
```
### "Issue not found"
```python
# Use get_issue_by_number, not get_issue
issue = backend.get_issue_by_number(42) # Correct
# issue = backend.get_issue("42") # Wrong - needs backend_id
```
## Full Documentation
- **Integration Guide:** `AGENT_INTEGRATION.md` (comprehensive patterns and strategies)
- **API Reference:** `CLAUDE.md` (for developers extending the capability)
- **Examples:** `examples/agents/` (working agent implementations)
- **Roadmap:** `ROADMAP.md` (upcoming features)
## Current Limitations (v1.0)
Be aware of these limitations:
1. **Manual Configuration:** Backend must be configured before use (auto-detect in v1.1)
2. **User Context:** Uses hardcoded user for CLI operations (agent identity in v1.2)
3. **No Built-in Locking:** Use assignee + comment workaround for claiming (native in v1.2)
4. **Basic Conflicts:** Manual resolution required for complex sync conflicts (advanced in v2.0)
Workarounds are documented in `AGENT_INTEGRATION.md`.
## Questions?
If you're unsure whether to use this capability for something:
**ASK:** "Does this involve creating, reading, updating, or searching issues?"
- **YES** → Use this capability
- **NO** → You can use other methods
**Example:**
- "Create an issue for the bug I found" → **Use issue-facade**
- "Read the project README" → Don't need issue-facade
- "Check if issue #42 exists" → **Use issue-facade**
- "Clone the repository" → Don't need issue-facade

View File

@@ -0,0 +1,6 @@
Use the issue-facade capability for all issue tracking operations.
**Quick reference:** See `.claude/capabilities/issue-facade.md`
**Examples:** See `capabilities/issue-facade/examples/agents/`
**DO NOT use direct API calls or platform CLIs!**

View File

@@ -0,0 +1,8 @@
# Available Capabilities
## Issue Tracking: issue-facade
**CRITICAL:** Always use this for issue operations. Never bypass with direct API calls.
**Docs:** `.claude/capabilities/issue-facade.md`
**Usage:** `/use-issues`

20
.claude/rules/agents.md Normal file
View File

@@ -0,0 +1,20 @@
## Kaizen Agents
Specialized agent personas available on demand via the state-hub MCP.
**Discover:** `list_kaizen_agents()` — returns all agents with name, description, category
**Load:** `get_kaizen_agent("tdd-workflow")` — returns full instructions; read and follow them
Common agents:
| Agent | Category | When to use |
|-------|----------|-------------|
| `tdd-workflow` | testing | Step-by-step TDD8 workflow for any feature |
| `code-refactoring` | quality | Code quality analysis and safe refactoring |
| `test-maintenance` | testing | Diagnose and fix failing tests |
| `requirements-engineering` | process | Prevent interface/mock mismatches upfront |
| `keepaTodofile` | process | Maintain TODO.md during work |
| `project-management` | process | Track status, determine next steps |
| `datamodel-optimization` | quality | Optimize dataclasses and data structures |
All 17 agents: call `list_kaizen_agents()` for the full list.

View File

@@ -0,0 +1,8 @@
## Architecture
<!-- TODO: Describe the key design decisions and component structure.
Key modules, data flows, external integrations, state machines, etc. -->
## Quick Reference
`~/state-hub/mcp_server/TOOLS.md` — MCP tool reference

View File

@@ -0,0 +1,38 @@
## First Session Protocol
Triggered when `get_domain_summary("markitect")` shows **no workstreams**.
The project is registered but work has not yet been structured.
**Step 1 — Read, don't write**
- `~/the-custodian/canon/projects/markitect/project_charter_v0.1.md` — purpose, scope
- `~/the-custodian/canon/projects/markitect/roadmap_v0.1.md` — planned phases
- Scan repo root: README, directory structure, existing code or docs
**Step 2 — Survey in-progress work**
Look for TODOs, open branches, half-finished files. Note done vs. started but incomplete.
**Step 3 — Propose workstreams to Bernd**
Propose 13 workstreams — each a coherent strand, weeks to months, anchored to a
roadmap phase. **Wait for approval before creating.**
**Step 4 — Create workplan file first, then DB record (ADR-001)**
```
workplans/markitect-project-WP-NNNN-<slug>.md ← write this first
```
Then register in the hub:
```
create_workstream(topic_id="5571d954-0d30-4950-980d-7bcaaad8e3e2", title="...", owner="...", description="...")
create_task(workstream_id="<id>", title="...", priority="high|medium|low")
```
**Step 5 — Record the setup**
```
add_progress_event(
summary="First session: structured markitect into N workstreams, M tasks",
event_type="milestone",
topic_id="5571d954-0d30-4950-980d-7bcaaad8e3e2",
detail={"workstreams": [...], "tasks_created": M}
)
```
<!-- Delete or archive this file once past first session -->

View File

@@ -0,0 +1,8 @@
## Repo boundary
This repo owns **markitect-main** only. It does not own:
<!-- TODO: List what belongs in adjacent repos, e.g.:
- SSH key management → railiance-infra/
- State hub code → state-hub/
-->

View File

@@ -0,0 +1,5 @@
**Purpose:** Knowledge artifact management system. Handles structured content creation, versioning, and publication workflows for the markitect domain.
**Domain:** markitect
**Repo slug:** markitect-project
**Topic ID:** 5571d954-0d30-4950-980d-7bcaaad8e3e2

View File

@@ -0,0 +1,84 @@
## Session Protocol
State Hub: http://127.0.0.1:8000
**Step 1 — Orient**
Read the offline-safe brief first — it works without a live hub connection:
```bash
cat .custodian-brief.md
```
Then call the MCP tool for richer cross-domain context when MCP tools are exposed:
```
get_domain_summary("markitect")
```
If MCP tools are unavailable in the current agent session, use the REST API:
```bash
curl -s "http://127.0.0.1:8000/state/summary" | python3 -m json.tool
```
If the hub is offline: `cd ~/state-hub && make api`
**Step 2 — Check inbox**
With MCP tools:
```
get_messages(to_agent="markitect-project", unread_only=True)
```
Mark read with `mark_message_read(message_id)`. Reply or act on coordination
requests before proceeding.
Without MCP tools:
```bash
curl -s "http://127.0.0.1:8000/messages/?to_agent=markitect-project&unread_only=true" \
| python3 -m json.tool
curl -s -X PATCH "http://127.0.0.1:8000/messages/<id>/read" \
-H "Content-Type: application/json" -d '{}'
```
**Step 3 — Scan workplans**
```bash
ls workplans/
```
For each file with `status: ready`, `active`, or `blocked`, note pending
`todo`/`in_progress` tasks.
**Step 4 — Present brief**
1. **Active workstreams** for `markitect` — title, task counts, blocking decisions
2. **Pending tasks** from `workplans/` + any `[repo:markitect-project]` hub tasks
3. **Goal guidance** — if `goal_guidance` in summary:
- `needs_workplan`: surface as top action — *"Repo goal '{title}' has no workplan yet"*
- `alignment_warnings`: flag if active work is not aligned with current goal
4. **Suggested next action** — highest-priority open item
5. **SBOM status** — flag if `last_sbom_at` is unset for this repo
If no workstreams: follow First Session Protocol (`first-session.md`).
**During work:** `record_decision()` · `add_progress_event()` · `resolve_decision()`
> State Hub is a *read model*. Bootstrap tools (`create_workstream`, `create_task`)
> are First Session Protocol only. Work structure belongs in repo files (ADR-001).
**Session close:**
With MCP tools:
```
add_progress_event(summary="...", topic_id="5571d954-0d30-4950-980d-7bcaaad8e3e2", workstream_id="<uuid>")
```
Without MCP tools:
```bash
curl -s -X POST http://127.0.0.1:8000/progress/ \
-H "Content-Type: application/json" \
-d '{"topic_id":"5571d954-0d30-4950-980d-7bcaaad8e3e2","workstream_id":"<uuid>","event_type":"note","summary":"what changed","author":"codex"}'
```
If workplan files were modified, ensure the local copy is up to date first:
```bash
git -C <repo_path> pull --ff-only
cd ~/state-hub && make fix-consistency REPO=markitect-project
```
For repos where implementation runs on a remote machine (e.g. CoulombCore),
use the combined target which pulls before fixing:
```bash
cd ~/state-hub && make fix-consistency-remote REPO=markitect-project
```
**C-15** (DB task ahead of file) is normal in multi-machine workflows — writeback
will sync the file to match DB. **C-16** (repo behind remote) blocks all writes
until you pull — intentional to prevent clobbering remote progress.

View File

@@ -0,0 +1,19 @@
## Stack
<!-- TODO: Fill in language, frameworks, and key dependencies -->
- **Language:**
- **Key deps:**
## Dev Commands
```bash
# TODO: Fill in the standard commands for this repo
# Install dependencies
# Run tests
# Lint / type check
# Build / package (if applicable)
```

View File

@@ -0,0 +1,28 @@
## Workplan Convention (ADR-001)
File location: `workplans/markitect-project-WP-NNNN-<slug>.md`
ID prefix: `MARKITECT-WP`
Work items originate as files in this repo **before** being registered in the hub.
Canonical workplan/workstream frontmatter statuses are:
`proposed`, `ready`, `active`, `blocked`, `backlog`, `finished`, `archived`.
Use `proposed` for a newly drafted plan, `ready` after review against current
repo state, and `finished` when implementation is complete. `stalled` and
`needs_review` are derived health labels, not stored statuses.
Closed workplans may be moved to `workplans/archived/` with a completion-date
prefix: `YYMMDD-markitect-project-WP-NNNN-<slug>.md`. The frontmatter id remains
unchanged; the prefix is only for quick visual reference.
Small opportunistic tasks discovered during another session use **Ad Hoc Tasks**:
`workplans/ADHOC-YYYY-MM-DD.md`, workstream slug `adhoc-YYYY-MM-DD`, and task ids
`ADHOC-YYYY-MM-DD-T01`, `T02`, etc. Use adhocs only for low-risk work completed
directly. Promote anything requiring analysis, design, approval, dependencies, or
multiple planned phases into a normal workplan.
Ecosystem todos from other agents arrive as `[repo:markitect-project]` hub tasks —
visible at session start. Pick one up by creating the workplan file, then registering
the workstream.
<!-- Ralph Loop rules and HEUREKA sequence: ~/.claude/CLAUDE.md — do not duplicate here -->

View File

@@ -10,7 +10,7 @@ principles with strict separation of concerns.
## Directory Structure & Clean Architecture
```
markitect_project/
markitect-main/
├── domain/ # Business logic (innermost layer)
├── application/ # Use cases and workflows
├── infrastructure/ # External interfaces (database, file system)

42
.custodian-brief.md Normal file
View File

@@ -0,0 +1,42 @@
<!-- custodian-brief: generated by fix-consistency — do not edit manually -->
# Custodian Brief — markitect-project
**Domain:** markitect
**Last synced:** 2026-05-03 17:31 UTC
**State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)*
## Active Workstreams
### TestDrive-JSUI — npm Publication
Progress: 0/9 done | workstream_id: `e203d487-01f1-494a-b14d-a436241a4c01`
**Open tasks:**
- · P.1 — Decide repository structure (monorepo vs standalone) `81c03377`
- · P.2 — Verify Markitect integration still works `f518601b`
- · P.3 — Resolve STANDALONE_PLAN.md status `d700098c`
- · P.4 — Pack and dry-run publish `94dd2a30`
- · P.5 — Create v1.0.0 release tag `d7c2ce00`
- · P.6 — Publish to npm and verify CDN `8bcde75e`
- · P.7 — Fresh install test in clean environment `61d14b53`
- … and 2 more open tasks
### Infospace Tooling — Stage 3 Close-out
Progress: 0/8 done | workstream_id: `830c888e-e1d4-43e6-8093-9b61e7578257`
**Open tasks:**
- · C.1 — Evaluate the 3 missing entities `0fa8f461`
- · C.2 — Run eval-summary and verify viability (6/6 PASS) `ba7d992c`
- · C.3 — Refresh metrics report from full 988-entity set `84a59244`
- · C.4 — Document advanced usage patterns `0ef75ee5`
- · C.5 — Add composition guide referencing supply-chain-vsm `864977db`
- · C.6 — Write performance notes `414496b0`
- · C.7 — S3.2: Complete clean per-chapter git history `21c865c1`
- … and 1 more open tasks
---
## MCP Orientation (when available)
If the state-hub MCP server is reachable, call:
`get_domain_summary("markitect")`
This provides richer cross-domain context.
If the MCP call fails, use this file as your orientation source.

6
.gitignore vendored
View File

@@ -78,6 +78,7 @@ Thumbs.db
# MarkiTect database files (local development)
markitect.db
**/infospace.db
assets/assets.db
**/assets.db
.markitect/
@@ -90,9 +91,14 @@ debug_*.py
# Claude Code local settings (user-specific permissions)
.claude/settings.local.json
# Claude Code runtime session locks (per-session, not content)
.claude/*.lock
.aider*
# API key files
apikey-*.txt
# TDDAI-specific ignores
ISSUES.index

12
.gitmodules vendored
View File

@@ -1,10 +1,14 @@
[submodule "wiki"]
path = wiki
url = http://92.205.130.254:32166/coulomb/markitect_project.wiki.git
url = http://92.205.130.254:32166/coulomb/markitect-main.wiki.git
branch = main
[submodule "capabilities/issue-facade"]
path = capabilities/issue-facade
url = http://92.205.130.254:32166/coulomb/issue-facade.git
[submodule "capabilities/kaizen-agentic"]
path = capabilities/kaizen-agentic
url = http://92.205.130.254:32166/coulomb/kaizen-agentic.git
[submodule "capabilities/testdrive-jsui"]
path = capabilities/testdrive-jsui
url = http://92.205.130.254:32166/coulomb/testdrive-jsui.git
[submodule "_issue-tracking/issue-facade"]
path = _issue-tracking/issue-facade
url = http://92.205.130.254:32166/coulomb/issue-facade.git
branch = main

162
AGENTS.md Normal file
View File

@@ -0,0 +1,162 @@
# markitect-main — Agent Instructions
## Repo Identity
**Purpose:** Knowledge artifact management system. Handles structured content creation, versioning, and publication workflows for the markitect domain.
**Domain:** markitect
**Repo slug:** markitect-project
**Topic ID:** `5571d954-0d30-4950-980d-7bcaaad8e3e2`
**Workplan prefix:** `MARKITECT-WP-`
---
## State Hub Integration
The Custodian State Hub tracks work across all domains. Interact via HTTP REST —
there is no MCP server for Codex agents.
| Context | URL |
|---------|-----|
| Local workstation | `http://127.0.0.1:8000` |
| Remote via tunnel | `http://127.0.0.1:18000` |
### Orient at session start
```bash
# Offline brief — works without hub connection
cat .custodian-brief.md
# Active workstreams for this domain
curl -s "http://127.0.0.1:8000/workstreams/?topic_id=5571d954-0d30-4950-980d-7bcaaad8e3e2&status=active" \
| python3 -m json.tool
# Check inbox
curl -s "http://127.0.0.1:8000/messages/?to_agent=markitect-project&unread_only=true" \
| python3 -m json.tool
```
Mark a message read:
```bash
curl -s -X PATCH "http://127.0.0.1:8000/messages/<id>/read" \
-H "Content-Type: application/json" -d '{}'
```
### Log progress (required at session close)
```bash
curl -s -X POST http://127.0.0.1:8000/progress/ \
-H "Content-Type: application/json" \
-d '{
"summary": "what was done",
"event_type": "note",
"author": "codex",
"workstream_id": "<uuid>",
"task_id": "<uuid>"
}'
```
Omit `workstream_id` / `task_id` when not applicable.
### Update task status
```bash
curl -s -X PATCH "http://127.0.0.1:8000/tasks/<task_id>" \
-H "Content-Type: application/json" \
-d '{"status": "in_progress"}'
# values: todo | in_progress | done | blocked
```
### Flag a task for human review
```bash
curl -s -X PATCH "http://127.0.0.1:8000/tasks/<task_id>" \
-H "Content-Type: application/json" \
-d '{"needs_human": true, "intervention_note": "reason"}'
```
---
## Session Protocol
**Start:**
1. `cat .custodian-brief.md` — domain goal and open workstreams (offline-safe)
2. Check inbox: `GET /messages/?to_agent=markitect-project&unread_only=true`; mark read
3. Scan workplans: `ls workplans/` — note `status: ready`, `active`, or `blocked` files and open tasks
4. Check blocked tasks: `GET /tasks/?needs_human=true`
**During work:**
- Update task statuses in workplan files as tasks progress
- Record significant decisions via `POST /decisions/`
**Close:**
1. Update workplan file task statuses to reflect progress
2. Log: `POST /progress/` with a summary of what changed
3. Note for the custodian operator: after workplan file changes, run from
`~/state-hub`:
```bash
make fix-consistency REPO=markitect-project
```
This syncs task status from files into the hub DB.
---
## Workplan Convention (ADR-001)
Work items originate as files in this repo — not in the hub. The hub is a
read/cache/index layer that rebuilds from files.
**File location:** `workplans/MARKITECT-WP-NNNN-<slug>.md`
**Archived location:** finished workplans may move to
`workplans/archived/YYMMDD-MARKITECT-WP-NNNN-<slug>.md`. The `YYMMDD` prefix is
the completion/archive date; the frontmatter `id` does not change.
**Ad Hoc Tasks:** small opportunistic fixes discovered during a session use
`workplans/ADHOC-YYYY-MM-DD.md` with task ids `ADHOC-YYYY-MM-DD-T01`, etc. Use
this only for low-risk work completed directly; create a normal workplan for
anything needing analysis, design, approval, dependencies, or multiple phases.
**Frontmatter:**
```yaml
---
id: MARKITECT-WP-NNNN
type: workplan
title: "..."
domain: markitect
repo: markitect-project
status: proposed | ready | active | blocked | backlog | finished | archived
owner: codex
topic_slug: ...
created: "YYYY-MM-DD"
updated: "YYYY-MM-DD"
state_hub_workstream_id: "<uuid>" # written by fix-consistency — do not edit
---
```
Use `proposed` for a new draft, `ready` after review against current repo
state, and `finished` after implementation. `stalled` and `needs_review` are
derived health labels, not frontmatter statuses.
**Task block format** (one per `##` section):
```
## Task Title
` ` `task
id: MARKITECT-WP-NNNN-T01
status: todo | in_progress | done | blocked
priority: high | medium | low
state_hub_task_id: "<uuid>" # written by fix-consistency — do not edit
` ` `
Task description text.
```
Status progression: `todo` → `in_progress` → `done` (or `blocked`)
To create a new workplan:
1. Write the file following the format above
2. Notify the custodian operator to run `make fix-consistency REPO=markitect-project`
(or send a message to the hub agent via `POST /messages/`)

View File

@@ -5,8 +5,106 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
See roadmap/YYMMDD-ROADMAPTOPIC/ directories for planning information like concepts, workplans, etc...
See history/YYMMDD-ROADMAOTOPIC/ directories for planning information of closed topics
## [Unreleased]
## [0.11.0] - 2026-01-06
### Added
- Release management optimizations: CHANGELOG validation, version-tag consistency checks
- Automated tag pushing with --push/--no-push flag
- Unpushed tags detection in release status
### Changed
- Improved release validation workflow with CHANGELOG schema validation
## [0.10.0] - 2026-01-06
### Added
- **Schema Management System**: Comprehensive schema management infrastructure with naming conventions and versioning
- Naming convention: `{domain}-schema-v{major}.{minor}.md` for all schemas
- Markdown-first schema format with embedded JSON (documentation + schema in one file)
- Schema catalog (`markitect/schemas/schema-catalog.yaml`) for metadata and discovery
- Terminology validation example (`examples/terminology/`) demonstrating schema usage beyond manpages
- Schema-of-schemas implementation archived in `history/2026-01-05-schema-of-schemas/`
- **Enhanced schema-list Command**: Now displays numbered references in all output formats for easy selection
- Simple format: `[1] schema-name.md` prefix for each schema
- Table format: `#` column as first column
- JSON/YAML: `number` field added to each schema
- Default format shows timestamps inline: `schema-name.json (added: 2026-01-04T23:01:19)`
- Table format includes Created/Updated columns
- Cleaner timestamp formatting (removed microseconds)
- **Multi-Schema Validation**: Enhanced schema-validate command with multiple selection methods
- Number selection: `markitect schema-validate 1` validates schema #1
- Range selection: `markitect schema-validate 1-3` validates schemas #1-3
- List selection: `markitect schema-validate 1,3,5` validates schemas #1,3,5
- Batch validation: `markitect schema-validate --all` validates all registered schemas
- Filename selection: `markitect schema-validate schema.md` from registry
- Filesystem path: `markitect schema-validate ./schema.md` from disk
- Batch results displayed as clear summary table with validation status
- Registry schemas take precedence over filesystem (with fallback)
- Full backward compatibility with existing single-file validation
- Enhanced control panel UI with better resize handle positioning for improved user interaction
- **Semantic Document Validation**: Complete semantic validation system for markdown documents against x-markitect schema extensions
- Section classification enforcement: required/recommended/optional/discouraged/improper sections validated
- Content pattern validation: required_patterns, forbidden_patterns, discouraged_patterns with regex matching
- Quality metrics checking: min_words, max_words, min_sentences validation with configurable thresholds
- Link validation: Internal/external link checking with configurable policies
- Internal links: Fragment anchors (#section) and file paths validated by default
- External links: HTTP/HTTPS validation with --check-links flag (opt-in, may be slow)
- Email validation: mailto: link format checking
- Broken link detection with line numbers and detailed error messages
- Modular validator architecture: SectionValidator, ContentValidator, LinkValidator with clean separation of concerns
- CLI integration: `--semantic/--no-semantic`, `--strict`, `--check-links` flags for validate command
- Comprehensive reporting: Detailed validation reports with errors/warnings, line numbers, matched text
- Test coverage: 25 tests for semantic validators (16 section/content + 9 link), 100% passing
- Full documentation: Semantic validation guide in SCHEMA_MANAGEMENT_GUIDE.md with examples
- Complements existing structural AST validation for complete document compliance checking
- **Changelog Schema**: Production schema for validating CHANGELOG.md files following Keep a Changelog format
- Schema file: `changelog-schema-v1.0.md` validates version history structure and formatting
- Enforces Unreleased section presence (required)
- Validates version format: `[X.Y.Z] - YYYY-MM-DD` with semantic versioning
- Validates change type subsections: Added, Changed, Deprecated, Removed, Fixed, Security
- Content pattern validation for version sections, date formats (ISO 8601), and change types
- Demonstrates real-world schema system usage: "The release that validates itself"
- Successfully validates project CHANGELOG.md with all semantic checks passing
### Changed
- **Directory Reorganization**:
- Renamed `todo/``roadmap/` for better organization of planning documents
- Completed schema-of-schemas implementation archived to `history/2026-01-05-schema-of-schemas/`
- Moved completed planning artifacts to history for reference
- Refactored contents control architecture to use base class pattern properly for better code organization
- Updated all file references and paths to point to single source of truth in capabilities/testdrive-jsui/js/controls/ directory
### Fixed
- **Version Detection Issue**: Fixed `markitect --version` returning "unknown" instead of actual version
- Added `git_describe_command` to setuptools-scm configuration to filter version tags correctly
- Configured git describe to use `--match 'v*'` pattern to ignore non-version tags
- Version detection now works correctly with development versions (e.g., 0.9.1.dev76)
- **Missing v0.9.0 Git Tag**: Retroactively created v0.9.0 annotated tag on commit b9c1b90 from 2025-11-14
- Maintains version history integrity (CHANGELOG documented v0.9.0 but tag was missing)
- Enables proper version progression to v0.10.0
- Duplicate file structure issue by eliminating duplicate control files and consolidating to capabilities/ directory
- Contents panel scrollbar behavior - moved overflow-y: auto to correct container level so scrollbar only spans content area when panel reaches max-height
### Removed
- **BREAKING**: Legacy DocumentControls component from TestDrive JSUI plugin system - all control panel functionality now provided by enhanced control panels (ContentsControl, StatusControl, DebugControl, EditControl) with Reset All button functionality moved to EditControl for better maintainability and elimination of code duplication
### Completed Features
- **Schema-of-Schemas Implementation** (All 6 Phases Complete ✅)
- ✅ Phase 1: Filename validation for schema naming convention (`markitect/schema_naming.py`, 50 tests)
- ✅ Phase 2: Markdown schema loader to parse `.md` schema files (`markitect/schema_loader.py`, 35 tests)
- ✅ Phase 3: Schema-for-schemas metaschema for schema validation (`schema-schema-v1.0.md`, 12 tests)
- ✅ Phase 4: Migration of 5 existing schemas to new format (migrated 2, deleted 3 duplicates)
- ✅ Phase 5: CLI enhancements - numbered schema-list, multi-schema validation with selection methods
- ✅ Phase 6: Integration testing and comprehensive documentation (SCHEMA_MANAGEMENT_GUIDE.md)
- **Total Test Coverage**: 97 tests, 100% passing
- **Complete Documentation**: Usage guide, naming spec, loader guide, metaschema reference
## [0.9.0] - 2025-11-14
### Added

11
CLAUDE.md Normal file
View File

@@ -0,0 +1,11 @@
# markitect-main — Claude Code Instructions
@SCOPE.md
@.claude/rules/repo-identity.md
@.claude/rules/session-protocol.md
@.claude/rules/first-session.md
@.claude/rules/workplan-convention.md
@.claude/rules/stack-and-commands.md
@.claude/rules/architecture.md
@.claude/rules/repo-boundary.md
@.claude/rules/agents.md

552
DEPENDENCIES.md Normal file
View File

@@ -0,0 +1,552 @@
# Dependencies
This document catalogs all dependencies used in the markitect project, describing how each library and tool is utilized. This helps coordinate dependencies with sister projects like referitect and maintain consistency across the ecosystem.
## Table of Contents
- [Core Dependencies](#core-dependencies)
- [Capability Dependencies](#capability-dependencies)
- [Development Dependencies](#development-dependencies)
- [JavaScript Dependencies](#javascript-dependencies)
- [Dependency Criticality Matrix](#dependency-criticality-matrix)
- [Coordination Guidelines for Sister Projects](#coordination-guidelines-for-sister-projects)
---
## Core Dependencies
These dependencies are required for markitect's core functionality.
### markdown-it-py
**Version:** Latest
**Criticality:** CRITICAL
**Purpose:** Core markdown parsing engine
**Usage:**
- Converts markdown content to Abstract Syntax Trees (AST)
- Enables table parsing with `.enable(['table'])`
- Uses CommonMark parser with token conversion
- Foundation for all markdown processing operations
**Files:** `markitect/parser.py`
**Why this matters:** This is the primary markdown parser. Sister projects should use the same parser for consistent AST structure and compatibility.
---
### PyYAML
**Version:** Latest
**Criticality:** CRITICAL
**Purpose:** YAML configuration parsing and serialization
**Usage:**
- Parses YAML frontmatter from markdown documents
- Loads configuration files for themes and workspaces
- Supports YAML-based schema definitions
- Safe loading of untrusted YAML data (`yaml.safe_load()`)
- Configuration serialization and deserialization
**Files:**
- `markitect/cli.py`
- `markitect/schema_loader.py`
- `markitect/matter_frontmatter/parser.py`
- `markitect/config_manager.py`
- `markitect/production/configuration.py`
- `markitect/serializer.py`
- Multiple capability files
**Why this matters:** YAML is the primary configuration format. Consistent YAML handling ensures config compatibility across projects.
---
### click
**Version:** >=8.0.0
**Criticality:** CRITICAL
**Purpose:** Command-line interface framework
**Usage:**
- Primary CLI framework for all command-line commands
- Command groups, options, and decorators for `markitect` CLI
- Table output formatting (combined with tabulate)
- Command organization and help system
- Error handling and context management
- Profile management, finance tracking, release management, issue tracking commands
**Files:** 63 files including:
- `markitect/cli.py` (main CLI)
- `markitect/cli_utils.py`
- `markitect/asset_commands.py`
- `markitect/profile/commands.py`
- `markitect/finance/cli.py`
- `capabilities/release-management/src/release_management/cli/main.py`
- `capabilities/kaizen-agentic/src/kaizen_agentic/cli.py`
- Issue tracker CLI commands
**Why this matters:** Click provides consistent CLI UX. Sister projects using Click will have familiar command patterns and help output.
---
### tabulate
**Version:** >=0.9.0
**Criticality:** HIGH
**Purpose:** ASCII table formatting for CLI output
**Usage:**
- Formats database query results as ASCII tables
- Provides multiple table formats: grid, simple, fancy
- Used in CLI output for:
- Database command results
- Performance metrics display
- Status reports
- File listings
- Financial tracking output
**Files:**
- `markitect/cli.py`
- `markitect/cli_utils.py`
- `markitect/plugins/builtin/markdown_commands.py`
- `markitect/finance/cli.py`
- `markitect/finance/worktime_commands.py`
**Why this matters:** Consistent table formatting across all CLI tools provides professional, readable output.
---
### jsonpath-ng
**Version:** >=1.5.0
**Criticality:** MEDIUM
**Purpose:** JSONPath querying for AST navigation
**Usage:**
- XPath-like navigation through AST structures
- Query parsing with `jsonpath-ng.parse()`
- Fallback simple dot-notation implementation if import fails
- Advanced query paradigm for structural queries
- Supports filters, recursive descent, array wildcards
**Files:**
- `markitect/query_paradigms/paradigms/jsonpath_paradigm.py`
- `markitect/ast_service.py`
- `markitect/graphql/resolvers.py`
**Why this matters:** Provides advanced AST querying. Optional but useful for complex document transformations.
---
### aiohttp
**Version:** >=3.8.0
**Criticality:** MEDIUM
**Purpose:** Async HTTP client for API interactions
**Usage:**
- HTTP session management with connection pooling
- Async/await pattern support for concurrent requests
- TCP connection optimization (limit, per-host limits)
- Timeout and keepalive configuration
- Authentication header management for API calls
- Gitea API integration
- Health check mechanisms
- Proper resource cleanup with async context managers
**Files:**
- `infrastructure/connection_manager.py`
**Why this matters:** Enables performant async I/O for API operations. Critical for scalable network operations.
---
### toml
**Version:** Latest
**Criticality:** LOW-MEDIUM
**Purpose:** TOML configuration format parsing
**Usage:**
- Parses TOML-formatted frontmatter in markdown files
- Alternative to YAML for configuration syntax
- Format detection: tries TOML if content has '=' and brackets
- Graceful fallback to YAML if TOML parsing fails
**Files:**
- `markitect/matter_frontmatter/parser.py`
- `capabilities/release-management/src/release_management/summary/generator.py`
- `capabilities/release-management/src/release_management/utils/validation.py`
- `capabilities/release-management/src/release_management/registries/factory.py`
**Why this matters:** Provides TOML support for users preferring TOML over YAML. Optional with fallback mechanisms.
---
### setuptools-scm
**Version:** >=8.0.0
**Criticality:** CRITICAL
**Purpose:** Git-based version management
**Usage:**
- Automatic version derivation from git tags
- Generates `_version.py` file during build
- Supports semantic versioning (python-simplified-semver scheme)
- Git tag format: `v*` (matches v0.11.0, v1.0.0, etc.)
- No local version suffix in scheme
- Used by release-management for version operations
**Files:**
- `markitect/__version__.py`
- `capabilities/release-management/src/release_management/utils/version.py`
- `capabilities/release-management/src/release_management/cli/main.py`
- `capabilities/release-management/src/release_management/utils/validation.py`
- `capabilities/release-management/src/release_management/core/builder.py`
**Why this matters:** Single source of truth for versioning. Critical for coordinated releases across sister projects.
---
## Capability Dependencies
Dependencies specific to optional capabilities. These extend markitect's functionality.
### markitect-utils
**Location:** `capabilities/markitect-utils`
**Dependencies:** None (intentionally dependency-free)
**Purpose:** Utility functions capability for testing ComposableRepositoryParadigm
---
### markitect-content
**Location:** `capabilities/markitect-content`
**Dependencies:**
- `click>=8.0.0` - CLI commands for content parsing and statistics
**Purpose:** Content parsing and statistics capability
---
### kaizen-agentic
**Location:** `capabilities/kaizen-agentic`
**Dependencies:**
- `pyyaml>=6.0` - Agent configuration files
- `click>=8.0.0` - CLI interface
- `pydantic>=2.0.0` - Data validation for agent configs
**Purpose:** AI agent development framework with continuous improvement patterns
**Key Usage:**
- Agent configuration via YAML
- CLI for agent management (`kaizen-agentic` command)
- Pydantic models for validated agent configurations
---
### release-management
**Location:** `capabilities/release-management`
**Dependencies:**
- `click>=8.0.0` - CLI commands
- `requests>=2.25.0` - Gitea package registry API
- `setuptools-scm>=8.0.0` - Version management
- `build>=0.8.0` - Python package building
- `packaging>=21.0` - Version parsing and comparison
- `pyyaml>=6.0` - Configuration files
- `pydantic>=2.0.0` - Configuration validation
**Purpose:** Comprehensive release management for Python projects
**Key Usage:**
- Package building (wheel, sdist)
- Git tag creation and management
- Gitea package registry publishing
- Version validation and semantic versioning
- Release configuration via YAML
**Entry Points:** `release`, `release-manager` commands
---
### testdrive-jsui
**Location:** `capabilities/testdrive-jsui`
**Dependencies:**
- `pytest>=7.0.0` - Test framework
- `pytest-xvfb>=3.0.0` - Headless browser testing
- `selenium>=4.0.0` - Browser automation
- `pathlib2>=2.3.0` (Python <3.4) - Path handling
**Purpose:** JavaScript UI testing framework capability
**Key Usage:**
- Browser-based testing infrastructure
- Headless browser execution
- Selenium WebDriver integration
- JavaScript test execution from Python
**Note:** Primarily a JavaScript library with Python adapter. See JavaScript Dependencies section.
---
### issue-facade (universal-issue-tracker)
**Location:** `_issue-tracking/issue-facade`
**Dependencies:**
- `click>=8.0.0` - CLI framework
- `requests>=2.25.0` - Remote backend API calls (Gitea, GitHub)
- `python-dateutil>=2.8.0` - Date/time parsing
**Purpose:** Backend-agnostic issue tracking with plugin architecture
**Key Usage:**
- Unified CLI for multiple issue backends (Gitea, GitHub, local SQLite)
- REST API integration
- Date parsing for issue timestamps
- Synchronization between local and remote backends
**Entry Points:** `issue`, `issue-tracker` commands
---
## Development Dependencies
These dependencies are only required during development and testing.
### Testing Frameworks
- `pytest>=6.0.0` - Primary test framework
- `pytest-cov>=2.0` - Coverage reporting
- `pytest-mock>=3.0` - Mocking utilities
- `pytest-randomly>=3.10.0` - Randomized test execution (kaizen-agentic)
- `pytest-asyncio>=0.21.0` - Async test support (testdrive-jsui)
- `pytest-timeout>=2.1.0` - Test timeout handling (testdrive-jsui)
### Code Quality
- `black>=22.0.0` - Code formatting (88 char line length)
- `isort>=5.0` - Import sorting
- `flake8>=5.0.0` - Linting (100 char max line length)
- `mypy>=1.0.0` - Static type checking
- `pre-commit>=2.0` - Git hooks
### Build Tools
- `setuptools>=64` - Python package building
- `wheel` - Wheel package format
- `build>=0.8.0` - PEP 517 build frontend
### Documentation
- `sphinx>=4.0` - Documentation generation (issue-facade)
- `sphinx-rtd-theme>=1.0` - Read the Docs theme
- `sphinx-click>=3.0` - Click command documentation
---
## JavaScript Dependencies
These dependencies support browser-side functionality and JavaScript testing.
### Runtime JavaScript Dependencies (Browser)
#### marked.js
**Version:** ^11.0.0 || ^12.0.0 || ^13.0.0 (peer dependency)
**Criticality:** MEDIUM
**Purpose:** Client-side markdown parsing
**Usage:**
- Real-time markdown rendering in browser
- Loaded via CDN: `https://cdn.jsdelivr.net/npm/marked/marked.min.js`
- Provides fallback if CDN fails
- HTML generation from markdown content with `marked.parse()`
**Files:**
- `markitect/templates/edit-mode.html`
- `markitect/templates/edit-mode-fixed.html`
- `markitect/templates/document.html`
**Why this matters:** Enables interactive markdown editing in browser. Sister projects with browser UI should use the same version for consistency.
---
### Node.js Development Dependencies
Located in `capabilities/testdrive-jsui/package.json`:
#### Build Tools
- `rollup@^4.53.5` - Module bundler
- `@rollup/plugin-babel@^6.1.0` - Babel transpilation
- `@rollup/plugin-commonjs@^29.0.0` - CommonJS support
- `@rollup/plugin-node-resolve@^16.0.3` - Node module resolution
- `@rollup/plugin-terser@^0.4.4` - Code minification
- `rollup-plugin-postcss@^4.0.2` - CSS processing
#### Testing
- `jest@^29.7.0` - JavaScript test framework
- `jest-environment-jsdom@^29.7.0` - DOM environment for tests
- `babel-jest@^29.7.0` - Babel integration for Jest
- `jsdom@^23.0.0` - DOM implementation for Node.js
#### Linting and Formatting
- `eslint@^8.57.0` - JavaScript linter
- `eslint-config-standard@^17.1.0` - Standard style guide
- `eslint-plugin-jest@^27.6.0` - Jest-specific rules
#### CSS Processing
- `postcss@^8.5.6` - CSS transformation
- `postcss-import@^16.1.1` - CSS import handling
- `autoprefixer@^10.4.23` - Vendor prefix automation
- `cssnano@^7.1.2` - CSS minification
#### Transpilation
- `@babel/core@^7.23.0` - JavaScript compiler
- `@babel/preset-env@^7.23.0` - Smart presets for target environments
---
## Dependency Criticality Matrix
| Criticality | Dependencies | Impact if Missing |
|:---|:---|:---|
| **CRITICAL** | markdown-it-py, PyYAML, click, setuptools-scm | Project cannot function |
| **HIGH** | tabulate | CLI output severely degraded |
| **MEDIUM** | jsonpath-ng, aiohttp, pydantic, requests, marked.js | Features unavailable, fallbacks exist |
| **LOW-MEDIUM** | toml | Optional format support |
| **DEV-ONLY** | pytest, black, flake8, mypy, jest, eslint | Development/testing affected only |
---
## Coordination Guidelines for Sister Projects
When developing referitect or other sister projects, follow these guidelines to maintain ecosystem consistency:
### Mandatory Alignment
These dependencies MUST match versions for compatibility:
1. **markdown-it-py** - Ensures consistent AST structure across projects
2. **PyYAML** - Enables shared configuration formats
3. **setuptools-scm** - Coordinated versioning strategy
4. **click** - Consistent CLI patterns and user experience
### Recommended Alignment
These dependencies SHOULD match for consistency:
1. **tabulate** - Uniform table output across CLIs
2. **pydantic** - Shared data validation patterns
3. **marked.js** - Consistent browser-side rendering
4. **black/flake8/mypy** - Unified code quality standards
### Optional Dependencies
Projects may choose different versions or alternatives:
1. **jsonpath-ng** - If advanced querying not needed
2. **aiohttp** vs. **requests** - Choose based on sync/async needs
3. **toml** - If TOML support not required
4. **selenium/pytest-xvfb** - If browser testing not needed
### Version Pinning Strategy
**Core dependencies:** Pin to minor versions (e.g., `click>=8.0.0`)
- Allows patch updates for security fixes
- Prevents breaking changes from major updates
**Development dependencies:** Pin to specific versions in lockfile
- Ensures reproducible development environments
- Update deliberately during maintenance windows
**Build dependencies:** Pin to exact versions (e.g., `setuptools>=64`)
- Ensures reproducible builds
- Critical for release management
### Capability Reuse
Sister projects can reuse these capabilities directly:
1. **release-management** - Use for package publishing
2. **kaizen-agentic** - Share agent development patterns
3. **issue-facade** - Unified issue tracking across projects
4. **testdrive-jsui** - Browser-based markdown editor
Install capabilities via local file references:
```toml
[project.dependencies]
release-management = {path = "../markitect-main/capabilities/release-management"}
```
### Shared Infrastructure
Both projects can share:
1. **Configuration schemas** - YAML/TOML format definitions
2. **CLI patterns** - Click command structure
3. **AST utilities** - Markdown parsing and transformation
4. **Testing infrastructure** - pytest configuration and fixtures
### Dependency Updates
When updating shared dependencies:
1. Update in markitect first
2. Test thoroughly
3. Document breaking changes in CHANGELOG.md
4. Coordinate update in referitect
5. Update this DEPENDENCIES.md file
---
## Dependency Graph
```
markitect (core)
├── markdown-it-py (AST generation)
├── PyYAML (config parsing)
├── click (CLI framework)
│ └── tabulate (table output)
├── jsonpath-ng (AST querying)
├── aiohttp (async HTTP)
├── toml (TOML parsing)
└── setuptools-scm (versioning)
capabilities/
├── markitect-utils (no deps)
├── markitect-content
│ └── click
├── kaizen-agentic
│ ├── pyyaml
│ ├── click
│ └── pydantic
├── release-management
│ ├── click
│ ├── requests
│ ├── setuptools-scm
│ ├── build
│ ├── packaging
│ ├── pyyaml
│ └── pydantic
├── testdrive-jsui
│ ├── pytest
│ ├── pytest-xvfb
│ ├── selenium
│ └── JavaScript deps:
│ ├── marked (peer)
│ ├── jest (dev)
│ ├── rollup (dev)
│ └── eslint (dev)
└── issue-facade
├── click
├── requests
└── python-dateutil
```
---
## License Compatibility
All dependencies use OSI-approved licenses compatible with MIT:
- **MIT License:** markdown-it-py, click, tabulate, jsonpath-ng, aiohttp, pydantic, marked.js, jest, rollup
- **Apache 2.0:** requests, setuptools-scm
- **BSD License:** PyYAML, selenium
- **PSF License:** python-dateutil (Python Software Foundation)
No GPL or copyleft licenses used - all dependencies are permissively licensed.
---
## Version History
- **v0.11.0** (2026-01-07): Initial DEPENDENCIES.md creation for referitect coordination
- Future updates will track dependency changes and version bumps
---
**Maintained by:** MarkiTect Project Team
**Last Updated:** 2026-01-07
**For Questions:** See main README.md or open an issue

253
INTRODUCTION.md Normal file
View File

@@ -0,0 +1,253 @@
# MarkiTect: Intelligent Markdown as Structured Information
## What is MarkiTect?
MarkiTect transforms how you work with markdown by treating documents as **structured, queryable information spaces** rather than plain text files. It's a comprehensive system for managing, validating, transforming, and composing markdown documents at scale—whether you're maintaining technical documentation, building knowledge bases, generating reports, or orchestrating complex document workflows.
Think of MarkiTect as a **database for your markdown content**, combined with powerful tools for ensuring consistency, automating repetitive tasks, and creating intelligent document systems that adapt to your needs.
## Why MarkiTect Matters
### The Problem
Modern documentation and content workflows face several challenges:
- **Inconsistency**: Different documents follow different structures, making content hard to maintain and process
- **Manual Validation**: No automated way to ensure documents meet quality standards or required formats
- **Content Silos**: Documents exist in isolation without relationships or shared context
- **Repetitive Work**: Copy-pasting boilerplate, manually updating cross-references, regenerating similar documents
- **Limited Automation**: Plain markdown files can't drive automated workflows or integrate with systems
- **Scaling Difficulties**: Managing hundreds or thousands of documents becomes overwhelming
### The MarkiTect Solution
MarkiTect solves these problems by treating markdown as **structured data**:
- **Schema-Driven**: Define document structures once, validate everywhere
- **Relational**: Documents can reference, include, and compose with each other
- **Queryable**: Extract insights from your content using powerful queries
- **Automated**: Generate, validate, and transform documents programmatically
- **Versioned**: Track changes and maintain document history
- **Composable**: Build complex documents from reusable components
## Core Capabilities
### 1. Information Spaces
Create **organized collections of related documents** that work together as cohesive knowledge spaces:
- **Structured Collections**: Group documents by project, topic, or any logical structure
- **Transclusion**: Embed content from one document into another, maintaining a single source of truth
- **Shared Context**: Variables and configuration shared across all documents in a space
- **Multiple Views**: Render the same content as HTML, export to directories, or query programmatically
- **Hierarchical Organization**: Parent-child relationships for inherited settings and permissions
### 2. Schema Management
Define and enforce document structures to ensure consistency:
- **Schema Generation**: Automatically extract schemas from example documents
- **Validation**: Verify documents match required structures before publishing
- **Schema Evolution**: Track how document requirements change over time
- **Template Generation**: Create document stubs from schemas
- **Semantic Understanding**: Schemas capture both structure and meaning
### 3. Document Processing
Powerful tools for analyzing, transforming, and manipulating content:
- **Intelligent Parsing**: Understand document structure including frontmatter, content, and metadata
- **Content Extraction**: Pull specific sections, metadata, or elements from documents
- **Transformation**: Convert between formats while preserving semantic meaning
- **Batch Operations**: Process multiple documents with a single command
- **Quality Checks**: Automated validation of links, references, and formatting
### 4. Transclusion & Composition
Build complex documents from reusable components:
- **Content Reuse**: Include shared sections across multiple documents
- **Variable Substitution**: Parameterize content for different contexts
- **Conditional Content**: Show or hide sections based on conditions
- **Variant Generation**: Create multiple versions from a single source
- **Cross-Document References**: Maintain relationships between related content
### 5. Querying & Analysis
Extract insights from your documentation:
- **Metadata Queries**: Find documents by attributes, tags, or properties
- **Content Search**: Locate specific information across your entire corpus
- **Relationship Mapping**: Understand how documents connect and depend on each other
- **Statistics**: Analyze document sizes, structures, and patterns
- **Reporting**: Generate summaries and reports from your content
### 6. Automation & Integration
Connect markdown content to broader workflows:
- **CLI Interface**: Automate any operation from scripts or CI/CD pipelines
- **GraphQL API**: Query and manipulate spaces programmatically
- **Event System**: React to document changes in real-time
- **Plugin Architecture**: Extend functionality for specific needs
- **Git Integration**: Optional version control for complete change history
## Use Cases
### Technical Documentation
**Challenge**: Maintain consistent API documentation across multiple services
**Solution**: Use schemas to enforce standard formats, transclusion for shared examples, and variables for service-specific details
**Benefits**:
- One source of truth for common patterns
- Automatic validation of all documentation
- Generate client libraries and API specs from the same source
- Track documentation changes alongside code
### Knowledge Management
**Challenge**: Organize company knowledge across departments and projects
**Solution**: Create information spaces for each domain, use cross-document references for relationships, enable search across all content
**Benefits**:
- Discoverable knowledge base
- Consistent structure across departments
- Track what content exists and how it connects
- Generate reports on documentation coverage
### Report Generation
**Challenge**: Create hundreds of similar reports with varying data
**Solution**: Define report schemas, use templates with variables, batch-generate reports from data sources
**Benefits**:
- Consistent report structure
- Automated generation from data
- Quality validation before distribution
- Version tracking for compliance
### Content Publishing
**Challenge**: Maintain a blog or publication with consistent formatting
**Solution**: Use schemas for post structures, transclusion for common elements, render to HTML with themes
**Benefits**:
- Enforced style guide compliance
- Reusable components (author bios, disclaimers, CTAs)
- Multiple output formats from single source
- Automated link checking and validation
### Educational Materials
**Challenge**: Create course materials with exercises, examples, and assessments
**Solution**: Schema-driven lesson structures, transclusion for common instructions, variable content for different audiences
**Benefits**:
- Consistent lesson structure
- Easy updates across multiple courses
- Generate student and instructor versions
- Track curriculum evolution
### Compliance Documentation
**Challenge**: Maintain audit trails and ensure required document elements
**Solution**: Strict schemas with validation, versioned spaces with history tracking, automated compliance reports
**Benefits**:
- Guaranteed document completeness
- Audit trail for all changes
- Automated compliance checking
- Generate reports for auditors
## Key Benefits
### For Writers and Content Creators
- **Focus on Content**: Let MarkiTect handle structure and consistency
- **Reuse Work**: Write once, use everywhere with transclusion
- **Quality Assurance**: Automated validation catches errors early
- **Easy Updates**: Change once, propagate everywhere
- **Version Control**: Track all changes with git integration
### For Teams and Organizations
- **Consistency at Scale**: Same structure across thousands of documents
- **Collaboration**: Shared spaces with inheritance and permissions
- **Automation**: Reduce manual work through scripting
- **Integration**: Connect documentation to development workflows
- **Maintainability**: Clear structure makes updates easier
### For Developers and Automators
- **Programmatic Access**: CLI and GraphQL APIs for automation
- **Event-Driven**: React to changes in real-time
- **Extensible**: Plugin system for custom functionality
- **Type-Safe**: Schemas provide structure for reliable automation
- **Performance**: Intelligent caching for fast operations
### For Organizations
- **Reduced Costs**: Automation reduces manual documentation work
- **Higher Quality**: Validation ensures consistency and completeness
- **Better Governance**: Track changes, enforce standards, maintain compliance
- **Improved Discovery**: Query and search across all content
- **Future-Proof**: Markdown is open, portable, and sustainable
## Getting Started
MarkiTect works with your existing markdown files. Start small:
1. **Process a document**: Ingest a markdown file to see its structure
2. **Generate a schema**: Extract patterns from your existing content
3. **Validate consistency**: Check if other documents match the pattern
4. **Create a space**: Group related documents together
5. **Explore capabilities**: Try transclusion, rendering, or querying
No migration required—MarkiTect enhances your workflow without replacing your tools.
## Who Should Use MarkiTect?
**MarkiTect is ideal for**:
- Technical writers maintaining large documentation sets
- Documentation teams standardizing content across projects
- Content operations teams automating publishing workflows
- Knowledge managers organizing institutional information
- Developers integrating documentation into CI/CD
- Publishers creating structured content from markdown
**MarkiTect might not be right if**:
- You only work with a handful of simple documents
- You don't need consistency or validation
- Your content has no structure or patterns
- You prefer visual editors over text-based workflows
## Philosophy
MarkiTect is built on these principles:
**Markdown is Data**: Documents contain structured information that can be processed, queried, and transformed
**Convention Over Configuration**: Sensible defaults that work out of the box, customize only when needed
**Schema-Driven Quality**: Define structures once, enforce them everywhere
**Composability**: Build complex systems from simple, reusable components
**Performance Matters**: Fast operations that scale to thousands of documents
**Open and Portable**: Pure markdown files that work with any tool
## What's Next?
This introduction provides a high-level overview of MarkiTect's value and capabilities. To dive deeper:
- **[CLI Tutorial](docs/CLI_TUTORIAL.md)**: Learn practical commands and workflows
- **[Schema Management Guide](docs/SCHEMA_MANAGEMENT_GUIDE.md)**: Master document validation
- **[Asset Management](docs/ASSET_MANAGEMENT_USER_GUIDE.md)**: Work with images and resources
- **[Plugin System](docs/PLUGIN_SYSTEM.md)**: Extend MarkiTect for your needs
- **[Project Structure](docs/PROJECT_STRUCTURE.md)**: Understand how it's organized
---
*MarkiTect transforms markdown from simple text files into intelligent, structured information spaces that power modern documentation and content workflows.*

129
SCOPE.md Normal file
View File

@@ -0,0 +1,129 @@
# SCOPE
> This file helps you quickly understand what this repository is about,
> when it is relevant, and when it is not.
> It is intentionally lightweight and may be incomplete.
---
## One-liner
Intelligent markdown engine and information management platform — treats documents as structured, queryable information spaces with schema validation, transclusion, LLM-driven evaluation, and infospace lifecycle management.
---
## Core Idea
MarkiTect turns fragmented knowledge (scattered docs, chats, notes) into structured, versioned, reusable artifacts. The core abstraction is an **infospace**: a curated collection of typed entities (concepts, mechanisms, observations) governed by a YAML config, validated against schemas, and evaluated for quality across five dimensions. The platform automates generation, validation, and transformation at scale, delegating domain-level judgment to LLMs while Python handles structure and evaluation.
---
## In Scope
- Parse, validate, and analyze markdown documents against schemas
- Generate schemas from example documents; enforce naming convention `{domain}-schema-v{major}.{minor}.md`
- Infospace lifecycle: create, populate, evaluate (per-entity + collection quality scores), compose, export
- Transclusion: embed content from one document into another, maintaining single source of truth
- LLM-driven prompt execution with dependency resolution and quality gates
- Relationship graph export (Mermaid, DOT) and analysis (networkx, FCA)
- Batch document processing; CLI (`markitect <command>`) and programmatic API
- Rendering: markdown → interactive HTML via plugin system (testdrive-jsui)
- Asset management (image embedding, resource handling)
---
## Out of Scope
- Visual/WYSIWYG editing (markdown-first, text-based workflows only)
- Real-time collaborative editing (git-based versioning instead)
- Financial transactions or external payment integration
- Making domain-level judgments in Python code (delegated to LLM via prompt templates)
- Storing secrets or credentials in plaintext
- Full GraphQL API (structure exists but not fully implemented)
- Vendor-specific integrations or lock-in
---
## Relevant When
- Managing large document sets (hundreds to thousands) needing consistent structure and validation
- Building or maintaining institutional knowledge bases, technical documentation, or canon releases
- Automating document generation from schemas or templates
- Tracking relationships and dependencies between knowledge artifacts
- Needing programmatic access to document structure (beyond file reading)
- Applying quality evaluation to a structured concept collection
---
## Not Relevant When
- Working with a handful of simple, unrelated documents
- Visual editor required
- Exclusively non-markdown source formats (PDF/Word need conversion first)
- No consistency, validation, or automation needed
---
## Current State
- Status: active (v0.13.0-dev, ~90 commits ahead of release)
- Implementation: substantial — core modules mature (CLI, parsing, schema management, prompt execution, infospace); infospace S3 close-out in progress; LLM adapter extracted to standalone `llm-connect` package
- Stability: stable core; plugin system and infospace tooling evolving; 200+ CHANGELOG entries since v0.6.0
- Usage: active personal development; examples with 988 entities and full evaluation pipeline
---
## How It Fits
- Upstream dependencies: `llm-connect` (LLM adapter library, extracted), `testdrive-jsui` (rendering plugin submodule), `markitect-utils` (utility library)
- Downstream consumers: Custodian — MarkiTect is the knowledge artifact platform in the canonical dependency order (Railiance → **Markitect** → Coulomb.social → Personhood/Foerster → Custodian)
- Often used with: the-custodian (state hub tracks markitect domain workstreams), kaizen-agentic (project-management agent for session workflow)
---
## Terminology
- Preferred terms: infospace, topic, discipline, entity, evaluation, viability, transclusion, schema, quality gates
- Also known as: "markitect", "the markdown engine"
- Potentially confusing terms: "topic" = the subject matter an infospace explains (not a chat thread); "discipline" = a reusable framework of concepts (itself a viable infospace); "infospace" ≠ filesystem directory (it's a curated conceptual collection with explicit quality thresholds)
---
## Related / Overlapping
- `llm-connect` — standalone LLM adapter extracted from MarkiTect (dependency)
- `the-custodian` — tracks markitect workstreams; custodian canon includes a markitect domain charter
- `marki-docx` — separate repo (on tegwick machine); relationship: docx export capability for MarkiTect artifacts
---
## Provided Capabilities
```capability
type: documentation
title: Structured document validation and schema management
description: Parse, validate, and enforce schemas on markdown documents — generate schemas from examples, validate entity collections, report naming convention compliance.
keywords: [markdown, schema, validation, document, structure, linting]
```
```capability
type: documentation
title: Infospace lifecycle management
description: Create, populate, evaluate (quality scores), compose, and export curated knowledge collections (infospaces) with transclusion and relationship graph analysis.
keywords: [infospace, knowledge, curation, evaluation, transclusion, quality, graph]
```
```capability
type: data
title: LLM-driven knowledge artifact generation
description: Execute prompts with dependency resolution and quality gates to generate typed entities — concepts, mechanisms, observations — at scale from schemas and templates.
keywords: [llm, generation, prompt, entity, artifact, knowledge, automation]
```
---
## Getting Oriented
- Start with: `CLAUDE.md` (dev commands, LLM config, infospace lifecycle), `INTRODUCTION.md` (use cases, philosophy)
- Key files / directories: `markitect/cli.py` (CLI entry point), `markitect/infospace/` (primary active area), `markitect/prompts/` (LLM execution), `roadmap/` (6 active planning tracks), `examples/infospace-with-history/` (988-entity reference implementation)
- Entry points: `markitect --help`; `markitect infospace --help`; `pytest tests/unit/` (inner TDD loop)

59
TODO.md
View File

@@ -6,16 +6,67 @@ The format is based on [Keep a Todofile V0.0.1](https://coulomb.social/open/Keep
The structure organizes **future tasks** by their impact, just as a changelog organizes past changes by their impact.
See roadmap/YYMMDD-ROADMAPTOPIC/ directories for planning information like concepts, workplans, etc...
***
## [Unreleased] - *Active Vibe-Coding State* 💡
This section is for tasks currently being discussed with or worked on by the coding assistant. These are the ephemeral, flow-of-thought tasks.
*No active tasks at this time.*
### Extract Capability-Capability from Issue-Facade (Paused)
***
**Context:** Issue-facade currently provides two capabilities:
1. **issue-tracking** (explicit in CAPABILITY-issue-tracking.yaml) - Issue management across platforms
2. **capability-capability** (implicit) - Patterns and tools for creating/managing capabilities
## Completed Tasks
The **capability-capability** includes:
- Feedback pattern (feedback/ directory, .capability/feedback CLI tool, documentation)
- Detachment facility (.capability/detach script for clean capability removal)
- Integration pattern (.capability/integrate.sh for project integration)
- CAPABILITY-*.yaml specification format
- ReusableCapabilitiesArchitecture.md (complete specification)
- Directory conventions (_family/implementation, visible/hidden patterns)
**Goal:** Extract capability-capability to separate `reusable-capability` repository so it can be used by any capability in the markitect ecosystem.
**Approach:** Step-by-step extraction, starting with specification.
#### Phase 1: Specification & Planning (Current)
- [ ] Create CAPABILITY-capability.yaml in issue-facade to explicitly declare the implicit capability
- [ ] Define what belongs to capability-capability family vs issue-tracking family
- [ ] Document the capability-capability API surface (what tools/patterns it provides)
- [ ] Identify all files/directories to extract
- [ ] Plan extraction strategy (copy vs move, how to maintain during transition)
#### Phase 2: Repository Creation
- [ ] Create reusable-capability repository structure
- [ ] Extract ReusableCapabilitiesArchitecture.md to new repo
- [ ] Extract feedback pattern (directory structure, CLI tool, README)
- [ ] Extract detachment facility (.capability/detach)
- [ ] Extract integration scripts (.capability/integrate.sh, integration-checklist.md)
- [ ] Create CAPABILITY-capability.yaml in new repo (canonical version)
- [ ] Add README.md for reusable-capability repo
#### Phase 3: Integration & Testing
- [ ] Update issue-facade to depend on reusable-capability (as integrated capability)
- [ ] Integrate reusable-capability into issue-facade using _capability/reusable-capability pattern
- [ ] Test that issue-facade still works with extracted capability
- [ ] Update issue-facade documentation to reference both capabilities it provides/uses
- [ ] Verify feedback system still works
- [ ] Verify detachment still works
#### Phase 4: Dogfooding & Validation
- [ ] Choose another markitect capability for dogfooding
- [ ] Integrate reusable-capability into that capability
- [ ] Add feedback system to new capability
- [ ] Add detachment facility to new capability
- [ ] Document learnings and refine reusable-capability based on real-world usage
- [ ] Update ReusableCapabilitiesArchitecture.md with insights
**Current Step:** Phase 1, Task 1 - Create CAPABILITY-capability.yaml
*Recent completed tasks have been documented in CHANGELOG.md following Keep a Changelog format.*

View File

@@ -15,7 +15,7 @@ You are responsible for:
### Directory Structure
```
markitect_project/
markitect-main/
├── Makefile # Main project Makefile
├── scripts/
│ └── capability_discovery.mk # Auto-discovery and delegation system

View File

@@ -0,0 +1,182 @@
---
name: project-assistant
description: Specialized assistant for project status, progress tracking, and development planning
---
## Instructions
You are the MarkiTect project assistant, specialized in providing project status overviews, tracking progress, and helping determine next steps for development work.
### Core Responsibilities
1. **Project Status Overview**: Provide concise summaries of current project state by analyzing key project files
2. **Progress Tracking**: Help understand what has been accomplished recently and what's currently in progress
3. **Next Steps Planning**: Suggest logical next actions based on project status and documented plans
### Key Project Files & Their Purpose
- **TODO.md**: Current state of implemenation based on the Keep-A-Todofile format for maintaining coding flow
- **CHANGELOG.md**: History of releases based on the Keep-A-Changelog format for easy access to what happend before
- **roadmap/**: Directory with current and close range roadmap-topic-directories for concepts, workplans, examples...
- **history/**: Directory with closed roadmap-topic-directories including finishd TODO.md files as YYMMDD-DONE.md
- **Makefile**: Provides helpers to use and improve the capabilities provided by the project
**Gitea Issues**: Backlog of issues and backlog of tasks stored as issues in gitea before selection as roadmap topics
### Project Infrastructure Knowledge
**Repository Structure:**
- Main project hosted on Gitea with issue tracking for use cases and tasks
- Planning documentation goes to roadmap/ROADMAPTOPIC subdirectories
- Closed roadmap-topic-directories git-mv to history/
- Auto generated documentation maintained in docs/
- Human generated documentation maintained in wiki/ submodule
- Test-driven development workflow with comprehensive test coverage
Important: Respect the directory structure! If in doubt ask or use directories under tmp/ to keep the structure clean!
**Development Workflow:**
- Issue-driven development using Gitea API integration
- Issue management via universal issue-facade CLI that works with multiple backends
- All commits require green test state
**Capability Inclusion Management:**
- **Internal Capabilities**: See `CAPABILITIES.md` for what MarkiTect provides to the world
- **External Capabilities**: Check `CAPABILITY_REGISTRY.md` for what MarkiTect uses
- **Before implementing**: Use `CLAUDE_CAPABILITY_REFERENCE.md` for quick lookup
- **Architecture Guide**: See `CAPABILITY_INCLUSION_GUIDE.md` for complete workflow
- **Discovery Tools**: `make capability-search TERM=xyz` to find existing functionality
**Issue Management Protocol:**
- **Gitea-First**: Feature requests, bugs, and enhancements should be documented as Gitea issues
- **Issue Creation**: When new requirements emerge, create issues in Gitea immediately but do NOT implement immediately
- **Strategic Planning**: Issues should be prioritized and scheduled based on project roadmap (history/ROADMAP.md)
- **Implementation Discipline**: Only work on issues that are explicitly planned for the current session
- **Issue Workflow**: Create → Triage → Plan → Schedule → Implement → Close
**TDD Workflow Management:**
- For issue management tasks, use the **issue-facade** system located in `capabilities/issue-facade/`
- The issue-facade provides unified CLI for GitHub, GitLab, Gitea, and local SQLite backends
- This includes sidequest management, test planning, and comprehensive development workflow guidance
### Response Guidelines
When asked about project status or next steps:
1. **Start with Current State**: Always check TODO.md for the latest activity
2. **Review Recent Progress**: Check CHANGELOG.md for previous work and progress
3. **Check Planned Work**: TODO.md documents next steps and priorities, if empty see topics in roadmap/
4. **Project Scope and Goals**: Vision, Mission, Guidelines and Usecases live in wiki/ if available
5. **Planning New Stuff**: Requirements (Epics and Stories) are gitea issues to be planned as roadmap topics
6. **Consider Git Status**: Allways be aware of current working directory state and recent commits
### Issue Management Guidelines
**When to Create Gitea Issues:**
- New feature requests or enhancement ideas emerge during development
- Bugs or technical debt are discovered but not immediately fixable
- Future improvements are identified but outside current session and topic scope
- Architecture decisions require documentation and future review
- Sidequests that we want to remember for later implementation
**Issue Creation Protocol:**
- Use descriptive titles that clearly state the requirement
- Include context: why is this needed, what problem does it solve
- Add relevant labels: enhancement, bug, documentation, technical-debt
- Reference related issues or components affected
- Do NOT implement immediately - issues are for tracking and planning
**Issue vs. Immediate Work:**
- Current session planned work: document in TODO.md and roadmap/ROADMAPTOPIC
- Discovered improvements: add to workplan in roadmap topic, continue with planned work
- Critical bugs affecting current work: fix immediately, then create issue for root cause analysis
- Future enhancements: note in roadmap-topic to create issues first for proper planning
- If possible create issues interactively when closing a topic, they are for human oversight and longterm
- Do not create issues for stuff that is detailed and can be adressed before closing the current topic
**Response Format:**
- Provide a brief status summary (2-3 sentences)
- Highlight recent progress or changes
- Suggest 1-3 concrete next actions based on documented plans
- Reference specific files and line numbers when relevant (e.g., `Next.md:8-12`)
### Example Response Structure
```
## Current Status
[Brief summary from ProjectStatusDigest.md]
## Recent Progress
[Key accomplishments from ProjectDiary.md latest entries]
## Recommended Next Steps
1. [Action from Next.md or logical progression]
2. [Secondary priority or alternative approach]
3. [Maintenance or validation task if applicable]
```
## Session Start-Up Protocol
When asked what's up for a new coding session, follow this standardized routine:
### Start-of-Session Checklist
1. **Mission Status**: Provide reminder to project vision and how we are doing
2. **Recently**: Provide reminder what we did last from the last entry to the diary
3. **TODO.md**: Check if we provided guidance for what to do next at the end of the last coding session
4. **git status**: Check if git is clean or work has been left unfinished
5. **Workspace clean**: Check if workspace is clean or we left of in the middle of a TDD cycle
6. **Topic or issue finished**: Check if we are currently working on a specific roadmap-topic or issue
7. **Suggestion**: Provide a sensible suggestion of what to do next
## Session Wrap-Up Protocol
When asked to help wrap up a development session, follow this standardized routine:
### End-of-Session Checklist:
2. **Update TODO.md**: Set clear priorities and strategy for next session using todofile format
3. **Update roadmap-topic directory information**: Refresh current status, metrics, and completed features
4. **Issue Management**: Review and create any issues for sidequests and discoveries made during session
5. **Anchor patterns**: Add Update suggestions for this project-assistant definition with any new workflow patterns
6. **Prepare for commit**: Ensure all documentation reflects current state
### Session Success Indicators:
- All tests passing (green state)
- Clear next steps documented
- Technical debt addressed or documented
- Progress measurably advanced toward project goals
### Wrap-Up Response Format:
```
## Session Summary
[Brief overview of accomplishments and current state]
## Documentation Updates
- ✅ TODO.md: [priorities set]
- ✅ roadmap/TOPIC files: [what was added or changed]
- ✅ CHANGELOG.ms: [status updated especially on release]
## Issues Created/Updated
- 🎯 Issue #X: [brief description] - [reason for creation]
- 📝 Issue #Y: [brief description] - [future enhancement]
## Next Session Preparation
[Clear guidance for resuming work next time]
Ready for commit: [list of files to commit]
```
### Example Capture Small Off-Topic Improvements in roadmap/eat-the-frog:
**Smell**: Different filename conventions od conflicting concepts, unclear guideance
**Hunch**: Ideas to explore that need consideration if useful and in scope
**Hickups**: Notes on inefficient or roundtripping implementation to analyse later
Collect these in the roadmap-topic-directory and move stuff to eat-the-frog on close if unfinished
### Example Issue Creation During Development:
**Scenario**: While implementing CLI commands, discover that error messages could be improved
**Action**: Create issue "Enhance CLI error messages with user-friendly formatting and suggestions"
**Result**: Continue with current CLI implementation, address error enhancement in future session
Generate issues for relevantly expensive or risky stuff and in direct feedback with developers.
Controled in-scope-work does not need the costly issue capture, refinement, selection roundtrip.
Remember: Your role is to help developers quickly understand "where we are" and "what should we do next" when picking up work on the MarkiTect project, and to ensure proper session wrap-up for continuity.

View File

@@ -1,165 +0,0 @@
---
name: project-assistant
description: Specialized assistant for project status, progress tracking, and development planning
---
## Instructions
You are the MarkiTect project assistant, specialized in providing project status overviews, tracking progress, and helping determine next steps for development work.
### Core Responsibilities
1. **Project Status Overview**: Provide concise summaries of current project state by analyzing key project files
2. **Progress Tracking**: Help understand what has been accomplished recently and what's currently in progress
3. **Next Steps Planning**: Suggest logical next actions based on project status and documented plans
### Key Project Files & Their Purpose
- **ProjectStatusDigest.md**: The canonical source of truth for project architecture, features, and current state
- **ProjectDiary.md**: Chronological record of major work packages, milestones, and development sessions
- **TODO.md**: Task management and priorities following Keep a Todofile format for maintaining coding flow
- **Makefile**: Provides helpers to use and improve the capabilities provided by the project
**Gitea Issues**: Backlog of issues and backlog of tasks stored as issues in gitea
### Project Infrastructure Knowledge
**Repository Structure:**
- Main project hosted on Gitea with issue tracking for use cases and tasks
- Documentation maintained in `wiki/` submodule
- Test-driven development workflow with comprehensive test coverage
**Development Workflow:**
- Issue-driven development using Gitea API integration
- Issue management via universal issue-facade CLI that works with multiple backends
- All commits require green test state
**Capability Inclusion Management:**
- **Internal Capabilities**: See `CAPABILITIES.md` for what MarkiTect provides to the world
- **External Capabilities**: Check `CAPABILITY_REGISTRY.md` for what MarkiTect uses
- **Before implementing**: Use `CLAUDE_CAPABILITY_REFERENCE.md` for quick lookup
- **Architecture Guide**: See `CAPABILITY_INCLUSION_GUIDE.md` for complete workflow
- **Discovery Tools**: `make capability-search TERM=xyz` to find existing functionality
**Issue Management Protocol:**
- **Gitea-First**: Feature requests, bugs, and enhancements should be documented as Gitea issues
- **Issue Creation**: When new requirements emerge, create issues in Gitea immediately but do NOT implement immediately
- **Strategic Planning**: Issues should be prioritized and scheduled based on project roadmap (history/ROADMAP.md)
- **Implementation Discipline**: Only work on issues that are explicitly planned for the current session
- **Issue Workflow**: Create → Triage → Plan → Schedule → Implement → Close
**TDD Workflow Management:**
- For issue management tasks, use the **issue-facade** system located in `capabilities/issue-facade/`
- The issue-facade provides unified CLI for GitHub, GitLab, Gitea, and local SQLite backends
- This includes sidequest management, test planning, and comprehensive development workflow guidance
### Response Guidelines
When asked about project status or next steps:
1. **Start with Current State**: Always check ProjectStatusDigest.md for the latest architecture and status
2. **Review Recent Progress**: Check ProjectDiary.md for recent accomplishments and context
3. **Check Planned Work**: Read Next.md for documented next steps and priorities
4. **Consider Git Status**: Be aware of current working directory state and recent commits
### Issue Management Guidelines
**When to Create Gitea Issues:**
- New feature requests or enhancement ideas emerge during development
- Bugs or technical debt are discovered but not immediately fixable
- Future improvements are identified but outside current session scope
- Architecture decisions require documentation and future review
- Sidequests that we want to remember for later implementation
**Issue Creation Protocol:**
- Use descriptive titles that clearly state the requirement
- Include context: why is this needed, what problem does it solve
- Add relevant labels: enhancement, bug, documentation, technical-debt
- Reference related issues or components affected
- Do NOT implement immediately - issues are for tracking and planning
**Issue vs. Immediate Work:**
- Current session planned work: implement directly (from Next.md)
- Discovered improvements: create issue, continue with planned work
- Critical bugs affecting current work: fix immediately, then create issue for root cause analysis
- Future enhancements: always create issue first for proper planning
**Response Format:**
- Provide a brief status summary (2-3 sentences)
- Highlight recent progress or changes
- Suggest 1-3 concrete next actions based on documented plans
- Reference specific files and line numbers when relevant (e.g., `Next.md:8-12`)
### Example Response Structure
```
## Current Status
[Brief summary from ProjectStatusDigest.md]
## Recent Progress
[Key accomplishments from ProjectDiary.md latest entries]
## Recommended Next Steps
1. [Action from Next.md or logical progression]
2. [Secondary priority or alternative approach]
3. [Maintenance or validation task if applicable]
Based on: ProjectStatusDigest.md:74-79, Next.md:7-13
```
## Session Start-Up Protocol
When asked what's up for a new coding session, follow this standardized routine:
### Start-of-Session Checklist
1. **Mission Status**: Provide reminder to project vision and how we are doing
2. **Recently**: Provide reminder what we did last from the last entry to the diary
3. **NEXT.txt**: Check if we provided guidance for what to do next at the end of the last coding session
4. **git status**: Check if git is clean or work has been left unfinished
5. **Workspace clean**: Check if workspace is clean or we left of in the middle of a TDD cycle
6. **Issue finished**: Check if we are currently working on a specific issue or need to select the next one
7. **Suggestion**: Provide a sensible suggestion of what to do next
## Session Wrap-Up Protocol
When asked to help wrap up a development session, follow this standardized routine:
### End-of-Session Checklist:
1. **Update ProjectDiary.md**: Add entry documenting progress, challenges, and achievements
2. **Update TODO.md**: Set clear priorities and strategy for next session using todofile format
3. **Update ProjectStatusDigest.md**: Refresh current status, metrics, and completed features
4. **Issue Management**: Review and create any issues for sidequests and discoveries made during session
5. **Anchor patterns**: Update this project-assistant definition with any new workflow patterns
6. **Prepare for commit**: Ensure all documentation reflects current state
### Session Success Indicators:
- All tests passing (green state)
- Clear next steps documented
- Technical debt addressed or documented
- Progress measurably advanced toward project goals
### Wrap-Up Response Format:
```
## Session Summary
[Brief overview of accomplishments and current state]
## Documentation Updates
- ✅ ProjectDiary.md: [what was added]
- ✅ Next.md: [priorities set]
- ✅ ProjectStatusDigest.md: [status updated]
## Issues Created/Updated
- 🎯 Issue #X: [brief description] - [reason for creation]
- 📝 Issue #Y: [brief description] - [future enhancement]
## Next Session Preparation
[Clear guidance for resuming work next time]
Ready for commit: [list of files to commit]
```
### Example Issue Creation During Development:
**Scenario**: While implementing CLI commands, discover that error messages could be improved
**Action**: Create issue "Enhance CLI error messages with user-friendly formatting and suggestions"
**Result**: Continue with current CLI implementation, address error enhancement in future session
Remember: Your role is to help developers quickly understand "where we are" and "what should we do next" when picking up work on the MarkiTect project, and to ensure proper session wrap-up for continuity.

View File

@@ -0,0 +1,51 @@
# Detachment Manifest
# This file records the removal of the issue-facade capability
# Use this information to re-integrate with updated architecture
detachment:
timestamp: 2025-12-17T21:23:14Z
capability_name: issue-facade
capability_family: issue-tracking
integration_pattern: capabilities-directory
original_location: /home/worsch/markitect-main/capabilities/issue-facade
capability_metadata:
spec_file: CAPABILITY-issue-tracking.yaml
version: unknown
implementation: unknown
maturity: unknown
integration_details:
parent_project: capabilities
parent_path: /home/worsch/markitect-main/capabilities
re_integration_guide: |
To re-integrate this capability using the new architecture:
# Option 1: Git submodule (recommended)
cd /home/worsch/markitect-main/capabilities
git submodule add <repo-url> _issue-facade
pip install -e _issue-facade/
# Option 2: Clone directly
cd /home/worsch/markitect-main/capabilities
git clone <repo-url> _issue-facade
pip install -e _issue-facade/
# Option 3: Copy into project
cd /home/worsch/markitect-main/capabilities
cp -r /path/to/issue-facade _issue-facade
pip install -e _issue-facade/
Note: Use underscore prefix (_issue-facade) per ReusableCapabilitiesArchitecture
notes:
- The original integration used pattern: capabilities-directory
- New architecture recommends: underscore-prefix at repo root
- See ReusableCapabilitiesArchitecture.md for details
repository_info:
# Fill in if re-integrating from git
git_url: "http://92.205.130.254:32166/coulomb/issue-facade.git" # e.g., https://github.com/markitect/issue-facade
git_branch: "main" # e.g., main
git_commit: "35daa514e59788250847cd706c43ea78f24c5c1d" # Optional: specific commit to use

View File

@@ -8,7 +8,7 @@ This test module validates outline mode schema generation improvements including
- Content instruction integration
- End-to-end workflow from example document to generated drafts
Created for Issue #46: https://gitea.coulomb.social/coulomb/markitect_project/issues/46
Created for Issue #46: https://gitea.coulomb.social/coulomb/markitect-main/issues/46
"""
import pytest

View File

@@ -209,7 +209,7 @@ tests/
## 🎯 Detailed File Structure After Migration
```
markitect_project/
markitect-main/
├── capabilities/
│ └── release-management/
│ ├── README.md ✅ CREATED

View File

@@ -162,7 +162,7 @@ clean_before_build = true
[tool.release-management.registries.gitea]
url = "http://92.205.130.254:32166"
owner = "coulomb"
repo = "markitect_project"
repo = "markitect-main"
auth_token_env = "GITEA_API_TOKEN"
[tool.release-management.registries.pypi]

View File

@@ -141,7 +141,7 @@ make release-publish VERSION=0.8.0
## Registry Information
- **Gitea URL**: http://92.205.130.254:32166
- **Repository**: coulomb/markitect_project
- **Repository**: coulomb/markitect-main
- **PyPI Registry URL**: http://92.205.130.254:32166/api/packages/coulomb/pypi
- **Package List URL**: http://92.205.130.254:32166/api/v1/packages/coulomb

View File

@@ -0,0 +1,10 @@
"""
CHANGELOG management tools.
This package provides tools for working with CHANGELOG.md files.
"""
from .editor import ChangelogEditor
from .parser import ChangelogParser
__all__ = ['ChangelogEditor', 'ChangelogParser']

View File

@@ -0,0 +1,207 @@
"""
CHANGELOG.md editor for programmatic updates.
This module provides tools for editing CHANGELOG.md files following
the Keep a Changelog format.
"""
from datetime import datetime
from pathlib import Path
from typing import Optional, List
class ChangelogEditor:
"""Programmatic editor for CHANGELOG.md files."""
def __init__(self, changelog_path: Optional[Path] = None):
"""Initialize changelog editor.
Args:
changelog_path: Path to CHANGELOG.md file
"""
self.changelog_path = changelog_path or Path.cwd() / 'CHANGELOG.md'
def create_version_section(self, version: str, date: Optional[str] = None) -> bool:
"""Create new version section and move Unreleased content.
Args:
version: Version number (e.g., "0.11.0")
date: Release date in YYYY-MM-DD format (defaults to today)
Returns:
True if successful, False otherwise
"""
if date is None:
date = datetime.now().strftime("%Y-%m-%d")
# Validate version format
version_clean = version.lstrip('v')
if not self.changelog_path.exists():
print(f"❌ CHANGELOG.md not found at {self.changelog_path}")
return False
try:
with open(self.changelog_path) as f:
lines = f.readlines()
# Find Unreleased section
unreleased_idx = None
for i, line in enumerate(lines):
if line.strip() == "## [Unreleased]":
unreleased_idx = i
break
if unreleased_idx is None:
print("❌ No [Unreleased] section found in CHANGELOG.md")
return False
# Find next version section or end of Unreleased content
next_section_idx = None
for i in range(unreleased_idx + 1, len(lines)):
if lines[i].startswith("## [") and not lines[i].startswith("## [Unreleased]"):
next_section_idx = i
break
# Extract Unreleased content (skip the header line and first blank line)
if next_section_idx:
unreleased_content = lines[unreleased_idx + 1:next_section_idx]
else:
unreleased_content = lines[unreleased_idx + 1:]
# Check if there's actual content to move
has_content = any(line.strip() and not line.strip().startswith('#')
for line in unreleased_content)
if not has_content:
print(f"⚠️ [Unreleased] section is empty. Add changes before creating release section.")
print(f"💡 Tip: You can still create the section, but it will be empty.")
# Create new version section with moved content
new_section_lines = [
f"## [{version_clean}] - {date}\n",
]
# Add the unreleased content (preserving structure)
new_section_lines.extend(unreleased_content)
# Ensure proper spacing after new section
if new_section_lines and not new_section_lines[-1].endswith('\n\n'):
if not new_section_lines[-1].endswith('\n'):
new_section_lines[-1] += '\n'
new_section_lines.append('\n')
# Build new file content
# Keep everything up to and including Unreleased header
new_lines = lines[:unreleased_idx + 1]
# Add blank line after Unreleased header
new_lines.append('\n')
# Add the new version section
new_lines.extend(new_section_lines)
# Add remaining sections (if any)
if next_section_idx:
new_lines.extend(lines[next_section_idx:])
# Write back
with open(self.changelog_path, 'w') as f:
f.writelines(new_lines)
print(f"✅ Created section [{version_clean}] - {date} in CHANGELOG.md")
if has_content:
print(f"📝 Moved content from [Unreleased] to [{version_clean}]")
print(f"💡 [Unreleased] section is now empty and ready for new changes")
return True
except Exception as e:
print(f"❌ Error editing CHANGELOG.md: {e}")
return False
def get_version_content(self, version: str) -> Optional[List[str]]:
"""Extract content for a specific version section.
Args:
version: Version number to extract (e.g., "0.10.0")
Returns:
List of lines in the version section, or None if not found
"""
version_clean = version.lstrip('v')
if not self.changelog_path.exists():
return None
try:
with open(self.changelog_path) as f:
lines = f.readlines()
# Find the version section
version_idx = None
for i, line in enumerate(lines):
if line.strip().startswith(f"## [{version_clean}]"):
version_idx = i
break
if version_idx is None:
return None
# Find next section
next_section_idx = None
for i in range(version_idx + 1, len(lines)):
if lines[i].startswith("## ["):
next_section_idx = i
break
# Extract content
if next_section_idx:
return lines[version_idx:next_section_idx]
else:
return lines[version_idx:]
except Exception:
return None
def has_unreleased_content(self) -> bool:
"""Check if Unreleased section has any content.
Returns:
True if Unreleased section has content, False otherwise
"""
if not self.changelog_path.exists():
return False
try:
with open(self.changelog_path) as f:
lines = f.readlines()
# Find Unreleased section
unreleased_idx = None
for i, line in enumerate(lines):
if line.strip() == "## [Unreleased]":
unreleased_idx = i
break
if unreleased_idx is None:
return False
# Find next section
next_section_idx = None
for i in range(unreleased_idx + 1, len(lines)):
if lines[i].startswith("## ["):
next_section_idx = i
break
# Check content
if next_section_idx:
content = lines[unreleased_idx + 1:next_section_idx]
else:
content = lines[unreleased_idx + 1:]
# Check if there's actual content (not just whitespace or section headers)
return any(line.strip() and not line.strip().startswith('#')
for line in content)
except Exception:
return False

View File

@@ -0,0 +1,179 @@
"""
CHANGELOG.md parser for extracting release notes.
This module provides tools for parsing CHANGELOG.md files and extracting
version-specific content for release notes.
"""
import re
from pathlib import Path
from typing import Optional
class ChangelogParser:
"""Parse CHANGELOG.md files and extract release information."""
def __init__(self, changelog_path: Optional[Path] = None):
"""Initialize changelog parser.
Args:
changelog_path: Path to CHANGELOG.md file
"""
self.changelog_path = changelog_path or Path.cwd() / 'CHANGELOG.md'
def extract_version_section(self, version: str, format: str = 'markdown') -> str:
"""Extract CHANGELOG section for a specific version.
Args:
version: Version to extract (e.g., "0.10.0")
format: Output format ('markdown', 'plain', 'html')
Returns:
Formatted content of the version section
"""
if not self.changelog_path.exists():
return f"Error: CHANGELOG.md not found at {self.changelog_path}"
try:
version_clean = version.lstrip('v')
with open(self.changelog_path) as f:
content = f.read()
# Find the version section using regex
# Match: ## [VERSION] - DATE followed by content until next ## [
pattern = rf"## \[{re.escape(version_clean)}\].*?\n\n(.*?)(?=\n## \[|\Z)"
match = re.search(pattern, content, re.DOTALL)
if not match:
return f"Error: No section found for version {version_clean} in CHANGELOG.md"
section_content = match.group(1).strip()
if not section_content:
return f"Warning: Section for version {version_clean} exists but is empty"
# Format based on requested format
if format == 'plain':
return self._to_plain(section_content)
elif format == 'html':
return self._to_html(section_content)
else:
return section_content # markdown (default)
except Exception as e:
return f"Error reading CHANGELOG: {e}"
def get_latest_version(self) -> Optional[str]:
"""Get the latest version number from CHANGELOG.
Returns:
Latest version string or None if not found
"""
if not self.changelog_path.exists():
return None
try:
with open(self.changelog_path) as f:
content = f.read()
# Find first version section (skip Unreleased)
pattern = r"## \[(\d+\.\d+\.\d+[^\]]*)\]"
match = re.search(pattern, content)
return match.group(1) if match else None
except Exception:
return None
def list_versions(self) -> list:
"""List all versions in CHANGELOG.
Returns:
List of version strings
"""
if not self.changelog_path.exists():
return []
try:
with open(self.changelog_path) as f:
content = f.read()
# Find all version sections (excluding Unreleased)
pattern = r"## \[(\d+\.\d+\.\d+[^\]]*)\]"
matches = re.findall(pattern, content)
return matches
except Exception:
return []
def _to_plain(self, markdown_content: str) -> str:
"""Convert markdown content to plain text.
Args:
markdown_content: Markdown formatted content
Returns:
Plain text content
"""
# Remove markdown formatting
plain = markdown_content
# Remove bold/italic
plain = re.sub(r'\*\*([^*]+)\*\*', r'\1', plain) # bold
plain = re.sub(r'\*([^*]+)\*', r'\1', plain) # italic
plain = re.sub(r'__([^_]+)__', r'\1', plain) # bold (underscores)
plain = re.sub(r'_([^_]+)_', r'\1', plain) # italic (underscores)
# Remove links but keep text
plain = re.sub(r'\[([^\]]+)\]\([^\)]+\)', r'\1', plain)
# Remove inline code backticks
plain = re.sub(r'`([^`]+)`', r'\1', plain)
# Convert headers to plain text with spacing
plain = re.sub(r'^### (.+)$', r'\n\1:', plain, flags=re.MULTILINE)
plain = re.sub(r'^## (.+)$', r'\n\1\n' + '=' * 40, plain, flags=re.MULTILINE)
return plain.strip()
def _to_html(self, markdown_content: str) -> str:
"""Convert markdown content to HTML.
Args:
markdown_content: Markdown formatted content
Returns:
HTML formatted content
"""
try:
import markdown
return markdown.markdown(markdown_content)
except ImportError:
# Fallback to basic HTML conversion if markdown package not available
html = markdown_content
# Headers
html = re.sub(r'^### (.+)$', r'<h3>\1</h3>', html, flags=re.MULTILINE)
html = re.sub(r'^## (.+)$', r'<h2>\1</h2>', html, flags=re.MULTILINE)
# Bold/italic
html = re.sub(r'\*\*([^*]+)\*\*', r'<strong>\1</strong>', html)
html = re.sub(r'\*([^*]+)\*', r'<em>\1</em>', html)
# Links
html = re.sub(r'\[([^\]]+)\]\(([^\)]+)\)', r'<a href="\2">\1</a>', html)
# Code
html = re.sub(r'`([^`]+)`', r'<code>\1</code>', html)
# Lists
html = re.sub(r'^- (.+)$', r'<li>\1</li>', html, flags=re.MULTILINE)
html = re.sub(r'(<li>.*</li>)', r'<ul>\1</ul>', html, flags=re.DOTALL)
# Paragraphs
html = re.sub(r'\n\n', '</p><p>', html)
html = f'<p>{html}</p>'
return html

View File

@@ -11,6 +11,9 @@ from typing import Optional
from ..core.manager import ReleaseManager
from ..utils.version import VersionManager
from ..changelog.editor import ChangelogEditor
from ..changelog.parser import ChangelogParser
from ..summary.generator import SummaryGenerator
@click.group(invoke_without_command=True)
@@ -55,6 +58,15 @@ def status(ctx):
print(f"Latest Commit: {status_info['latest_commit']}")
print(f"Latest Tag: {status_info['latest_tag'] or 'None'}")
print(f"Uncommitted Changes: {'Yes' if status_info['has_changes'] else 'No'}")
# Show unpushed tags
unpushed_tags = status_info.get('unpushed_tags', [])
if unpushed_tags:
print(f"\n⚠️ Unpushed Tags: {len(unpushed_tags)} tag(s) not pushed to origin")
for tag in unpushed_tags:
print(f" - {tag}")
print(f"\n💡 Push tags with: git push origin {' '.join(unpushed_tags)}")
print(f" Or push all tags: git push --tags")
else:
print("Git Repository: Not available")
@@ -104,8 +116,10 @@ def validate(ctx):
@main.command()
@click.option('--version', required=True, help='Version to tag (e.g., 0.8.0)')
@click.option('--message', help='Tag message')
@click.option('--push/--no-push', default=True,
help='Automatically push tag to origin (default: --push)')
@click.pass_context
def tag(ctx, version: str, message: Optional[str]):
def tag(ctx, version: str, message: Optional[str], push: bool):
"""Create git tag for version."""
manager = ReleaseManager(
project_root=ctx.obj['project_root'],
@@ -113,8 +127,10 @@ def tag(ctx, version: str, message: Optional[str]):
force=ctx.obj['force']
)
if manager.create_tag(version, message):
if manager.create_tag(version, message, push=push):
print(f"✅ Successfully created tag for version {version}")
if not push:
print(f"💡 Push tag with: git push origin v{version}")
else:
print(f"❌ Failed to create tag for version {version}")
sys.exit(1)
@@ -248,5 +264,153 @@ def version_info(ctx, suggest: bool):
print(f" {key.replace('_', ' ').title()}: {value}")
@main.command('check-consistency')
@click.option('--version', required=True, help='Version to check (e.g., 0.10.0)')
@click.pass_context
def check_consistency(ctx, version: str):
"""Check consistency between CHANGELOG version and git tags."""
manager = ReleaseManager(
project_root=ctx.obj['project_root'],
dry_run=ctx.obj['dry_run'],
force=ctx.obj['force']
)
is_consistent, issues = manager.check_version_consistency(version)
if is_consistent:
print(f"✅ Version {version} is consistent:")
print(f" - CHANGELOG has section for {version}")
print(f" - Git tag v{version} exists")
print(f" - [Unreleased] section present")
else:
print(f"❌ Version {version} has consistency issues:")
for issue in issues:
print(f" - {issue}")
sys.exit(1)
@main.command('prepare')
@click.argument('version')
@click.option('--date', default=None, help='Release date (YYYY-MM-DD, defaults to today)')
@click.pass_context
def prepare(ctx, version: str, date: Optional[str]):
"""Prepare CHANGELOG for new version release.
Creates a new version section in CHANGELOG.md and moves all content
from the [Unreleased] section to the new version section.
"""
project_root = ctx.obj['project_root'] or Path.cwd()
changelog_path = project_root / 'CHANGELOG.md'
editor = ChangelogEditor(changelog_path)
# Create version section
if editor.create_version_section(version, date):
# Validate result
manager = ReleaseManager(
project_root=ctx.obj['project_root'],
dry_run=ctx.obj['dry_run'],
force=ctx.obj['force']
)
# Check if CHANGELOG is valid after edit
is_valid, issues = manager.validate_release_state()
if is_valid:
print("\n✅ CHANGELOG validation passed")
else:
print("\n⚠️ CHANGELOG validation issues after edit:")
for issue in issues:
if 'CHANGELOG' in issue:
print(f" - {issue}")
else:
print(f"❌ Failed to prepare CHANGELOG for version {version}")
sys.exit(1)
@main.command('summary')
@click.argument('version')
@click.option('--output', '-o', default=None, type=click.Path(path_type=Path),
help='Output file path (defaults to RELEASE_SUMMARY_vX.Y.Z.md)')
@click.pass_context
def summary(ctx, version: str, output: Optional[Path]):
"""Generate release summary document.
Extracts CHANGELOG content, git statistics, build artifacts, and
validation results to create a comprehensive release summary.
"""
project_root = ctx.obj['project_root'] or Path.cwd()
# Default output path
if output is None:
version_clean = version.lstrip('v')
output = project_root / f"RELEASE_SUMMARY_v{version_clean}.md"
elif not output.is_absolute():
output = project_root / output
generator = SummaryGenerator(project_root)
try:
content = generator.generate(version, output_path=output)
print(f"\n✅ Release summary generated successfully")
print(f"📄 Summary saved to: {output}")
except Exception as e:
print(f"❌ Failed to generate release summary: {e}")
sys.exit(1)
@main.command('notes')
@click.argument('version', required=False)
@click.option('--format', 'output_format', type=click.Choice(['markdown', 'plain', 'html']),
default='markdown', help='Output format (default: markdown)')
@click.option('--output', '-o', default=None, type=click.Path(path_type=Path),
help='Save to file instead of stdout')
@click.pass_context
def notes(ctx, version: Optional[str], output_format: str, output: Optional[Path]):
"""Extract release notes from CHANGELOG.md.
Extracts the CHANGELOG section for a specific version and outputs it
in various formats. Useful for creating GitHub/Gitea release notes.
If no version is specified, uses the latest version from CHANGELOG.
Examples:
release notes 0.10.0 # Extract v0.10.0 notes (markdown)
release notes # Extract latest version notes
release notes 0.10.0 --format plain # Plain text format
release notes 0.10.0 -o notes.md # Save to file
release notes 0.10.0 | gh release create v0.10.0 -F - # Pipe to gh
"""
project_root = ctx.obj['project_root'] or Path.cwd()
changelog_path = project_root / 'CHANGELOG.md'
parser = ChangelogParser(changelog_path)
# Get version (use latest if not specified)
if version is None:
version = parser.get_latest_version()
if version is None:
print("❌ Could not determine version from CHANGELOG.md")
sys.exit(1)
print(f"Using latest version: {version}", file=sys.stderr)
# Extract content
content = parser.extract_version_section(version, format=output_format)
# Check for errors
if content.startswith("Error:") or content.startswith("Warning:"):
print(content)
sys.exit(1 if content.startswith("Error:") else 0)
# Output
if output:
if not output.is_absolute():
output = project_root / output
output.write_text(content)
print(f"✅ Release notes written to {output}", file=sys.stderr)
else:
print(content)
if __name__ == '__main__':
main()

View File

@@ -75,12 +75,13 @@ class ReleaseManager:
"""
return self.validator.validate_release_state(force=self.force)
def create_tag(self, version: str, message: Optional[str] = None) -> bool:
def create_tag(self, version: str, message: Optional[str] = None, push: bool = True) -> bool:
"""Create a git tag for the release.
Args:
version: Version to tag (e.g., "1.0.0")
message: Optional tag message
push: Whether to push the tag to origin (default: True)
Returns:
True if tag created successfully, False otherwise
@@ -93,7 +94,16 @@ class ReleaseManager:
print(f" - {issue}")
return False
return self.git_manager.create_tag(version, message)
# Check version-tag consistency (ensure CHANGELOG has version section)
changelog_valid, changelog_issues = self.validator.validate_changelog_version(version)
if not changelog_valid and not self.force:
print(f"❌ Cannot create tag for version {version}:")
for issue in changelog_issues:
print(f" - {issue}")
print("\n💡 Tip: Add a section for version {version} to CHANGELOG.md before tagging")
return False
return self.git_manager.create_tag(version, message, push=push)
def build_packages(self) -> bool:
"""Build release packages.
@@ -212,4 +222,15 @@ class ReleaseManager:
Returns:
List of commit messages since last tag
"""
return self.git_manager.get_commits_since_tag()
return self.git_manager.get_commits_since_tag()
def check_version_consistency(self, version: str) -> Tuple[bool, List[str]]:
"""Check consistency between CHANGELOG version and git tags.
Args:
version: Version to check (e.g., "0.10.0")
Returns:
Tuple of (is_consistent, list_of_issues)
"""
return self.validator.check_version_tag_consistency(version)

View File

@@ -48,22 +48,27 @@ class GitManager:
except subprocess.CalledProcessError:
latest_tag = None
# Get unpushed tags
unpushed_tags = self.get_unpushed_tags()
return {
'is_repo': True,
'branch': current_branch,
'has_changes': has_changes,
'latest_commit': latest_commit,
'latest_tag': latest_tag
'latest_tag': latest_tag,
'unpushed_tags': unpushed_tags
}
except subprocess.CalledProcessError:
return {'is_repo': False}
def create_tag(self, version: str, message: Optional[str] = None) -> bool:
"""Create and push git tag.
def create_tag(self, version: str, message: Optional[str] = None, push: bool = True) -> bool:
"""Create and optionally push git tag.
Args:
version: Version to tag (e.g., "1.0.0")
message: Optional tag message
push: Whether to push the tag to origin (default: True)
Returns:
True if successful, False otherwise
@@ -81,16 +86,19 @@ class GitManager:
self._run_command(['git', 'tag', '-a', tag_name, '-m', tag_message])
print(f"✅ Tag {tag_name} created")
# Push tag to origin
try:
print(f"📤 Pushing tag to origin...")
self._run_command(['git', 'push', 'origin', tag_name])
print(f"✅ Tag pushed to origin")
return True
except subprocess.CalledProcessError as e:
print(f"⚠️ Could not push tag to origin: {e}")
print(f"You can push it manually with: git push origin {tag_name}")
return True # Tag created successfully, push can be done manually
# Push tag to origin if requested
if push:
try:
print(f"📤 Pushing tag to origin...")
self._run_command(['git', 'push', 'origin', tag_name])
print(f"✅ Tag pushed to origin")
return True
except subprocess.CalledProcessError as e:
print(f"⚠️ Could not push tag to origin: {e}")
print(f"You can push it manually with: git push origin {tag_name}")
return True # Tag created successfully, push can be done manually
else:
return True # Tag created successfully, user chose not to push
except subprocess.CalledProcessError as e:
print(f"❌ Failed to create tag: {e}")
@@ -178,6 +186,47 @@ class GitManager:
except subprocess.CalledProcessError:
return None
def get_unpushed_tags(self, remote: str = 'origin') -> List[str]:
"""Get list of tags that exist locally but not on remote.
Args:
remote: Remote name to compare against (default: 'origin')
Returns:
List of unpushed tag names
"""
try:
# Get local tags
local_result = self._run_command(['git', 'tag', '-l'])
local_tags = set(tag.strip() for tag in local_result.stdout.strip().split('\n') if tag.strip())
# Get remote tags
try:
remote_result = self._run_command(['git', 'ls-remote', '--tags', remote])
remote_lines = remote_result.stdout.strip().split('\n')
# Parse remote tags (format: "hash refs/tags/tagname")
remote_tags = set()
for line in remote_lines:
if not line:
continue
parts = line.split('refs/tags/')
if len(parts) > 1:
# Remove ^{} suffix for annotated tags
tag_name = parts[1].replace('^{}', '')
remote_tags.add(tag_name)
# Find tags that are local but not remote
unpushed = sorted(local_tags - remote_tags)
return unpushed
except subprocess.CalledProcessError:
# Remote not available, assume all tags are unpushed
return sorted(local_tags)
except subprocess.CalledProcessError:
return []
def _run_command(self, cmd: List[str]) -> subprocess.CompletedProcess:
"""Run a git command.

View File

@@ -0,0 +1,9 @@
"""
Release summary generation tools.
This package provides tools for generating release summary documents.
"""
from .generator import SummaryGenerator
__all__ = ['SummaryGenerator']

View File

@@ -0,0 +1,305 @@
"""
Release summary generator.
This module generates comprehensive release summary documents from
CHANGELOG content and git metadata.
"""
import subprocess
from datetime import datetime
from pathlib import Path
from typing import Optional, Dict, Any, List
import re
class SummaryGenerator:
"""Generate release summary documents."""
def __init__(self, project_root: Optional[Path] = None):
"""Initialize summary generator.
Args:
project_root: Root directory of the project
"""
self.project_root = project_root or Path.cwd()
self.changelog_path = self.project_root / 'CHANGELOG.md'
self.dist_dir = self.project_root / 'dist'
def generate(self, version: str, output_path: Optional[Path] = None) -> str:
"""Generate release summary document.
Args:
version: Version to generate summary for (e.g., "0.10.0")
output_path: Optional path to write summary to
Returns:
Generated summary content
"""
version_clean = version.lstrip('v')
tag_name = f"v{version_clean}"
# Get components
changelog_section = self.extract_changelog_section(version_clean)
git_stats = self.get_git_statistics(tag_name)
build_artifacts = self.list_build_artifacts()
validation_results = self.get_validation_results()
# Determine project name
project_name = self._get_project_name()
# Build summary
summary = f"""# {project_name} {version_clean} Release Summary
**Release Date**: {git_stats.get('release_date', 'Unknown')}
**Git Tag**: {tag_name}
**Commit**: {git_stats.get('commit_hash', 'Unknown')}
---
## Changes
{changelog_section}
---
## Git Statistics
- **Commits**: {git_stats.get('commit_count', 0)} commit(s) since last release
- **Files Changed**: {git_stats.get('files_changed', 0)} file(s)
- **Insertions**: +{git_stats.get('insertions', 0)} lines
- **Deletions**: -{git_stats.get('deletions', 0)} lines
- **Contributors**: {git_stats.get('contributors', 'Unknown')}
---
## Build Artifacts
{build_artifacts}
---
## Validation
{validation_results}
---
**Generated**: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
"""
if output_path:
output_path.write_text(summary)
print(f"✅ Release summary written to {output_path}")
return summary
def extract_changelog_section(self, version: str) -> str:
"""Extract CHANGELOG section for a specific version.
Args:
version: Version to extract (e.g., "0.10.0")
Returns:
Markdown content of the version section
"""
if not self.changelog_path.exists():
return "⚠️ CHANGELOG.md not found"
try:
with open(self.changelog_path) as f:
content = f.read()
# Find the version section
pattern = rf"## \[{re.escape(version)}\].*?\n\n(.*?)(?=\n## \[|\Z)"
match = re.search(pattern, content, re.DOTALL)
if match:
section_content = match.group(1).strip()
return section_content if section_content else "No changes documented"
else:
return f"⚠️ No section found for version {version} in CHANGELOG.md"
except Exception as e:
return f"❌ Error reading CHANGELOG: {e}"
def get_git_statistics(self, tag: str) -> Dict[str, Any]:
"""Get git statistics for a release tag.
Args:
tag: Git tag name (e.g., "v0.10.0")
Returns:
Dictionary with git statistics
"""
stats = {}
try:
# Get tag date
try:
result = subprocess.run(
['git', 'log', '-1', '--format=%ci', tag],
capture_output=True, text=True, check=True, cwd=self.project_root
)
date_str = result.stdout.strip()
# Parse to get just the date
stats['release_date'] = date_str.split()[0] if date_str else 'Unknown'
except subprocess.CalledProcessError:
stats['release_date'] = 'Unknown'
# Get commit hash
try:
result = subprocess.run(
['git', 'rev-parse', tag],
capture_output=True, text=True, check=True, cwd=self.project_root
)
stats['commit_hash'] = result.stdout.strip()[:8]
except subprocess.CalledProcessError:
stats['commit_hash'] = 'Unknown'
# Find previous tag
try:
result = subprocess.run(
['git', 'describe', '--tags', '--abbrev=0', f'{tag}^'],
capture_output=True, text=True, check=True, cwd=self.project_root
)
previous_tag = result.stdout.strip()
except subprocess.CalledProcessError:
# No previous tag, use initial commit
previous_tag = None
# Get commit count
if previous_tag:
try:
result = subprocess.run(
['git', 'rev-list', '--count', f'{previous_tag}..{tag}'],
capture_output=True, text=True, check=True, cwd=self.project_root
)
stats['commit_count'] = int(result.stdout.strip())
except subprocess.CalledProcessError:
stats['commit_count'] = 0
else:
try:
result = subprocess.run(
['git', 'rev-list', '--count', tag],
capture_output=True, text=True, check=True, cwd=self.project_root
)
stats['commit_count'] = int(result.stdout.strip())
except subprocess.CalledProcessError:
stats['commit_count'] = 0
# Get file changes, insertions, deletions
if previous_tag:
diff_range = f'{previous_tag}..{tag}'
else:
diff_range = tag
try:
result = subprocess.run(
['git', 'diff', '--shortstat', diff_range],
capture_output=True, text=True, check=True, cwd=self.project_root
)
shortstat = result.stdout.strip()
# Parse shortstat: "X files changed, Y insertions(+), Z deletions(-)"
files_match = re.search(r'(\d+) files? changed', shortstat)
insert_match = re.search(r'(\d+) insertions?', shortstat)
delete_match = re.search(r'(\d+) deletions?', shortstat)
stats['files_changed'] = int(files_match.group(1)) if files_match else 0
stats['insertions'] = int(insert_match.group(1)) if insert_match else 0
stats['deletions'] = int(delete_match.group(1)) if delete_match else 0
except subprocess.CalledProcessError:
stats['files_changed'] = 0
stats['insertions'] = 0
stats['deletions'] = 0
# Get contributors
try:
result = subprocess.run(
['git', 'log', '--format=%an', f'{previous_tag}..{tag}' if previous_tag else tag],
capture_output=True, text=True, check=True, cwd=self.project_root
)
contributors = list(set(result.stdout.strip().split('\n')))
stats['contributors'] = ', '.join(contributors) if contributors and contributors[0] else 'Unknown'
except subprocess.CalledProcessError:
stats['contributors'] = 'Unknown'
except Exception as e:
print(f"⚠️ Error getting git statistics: {e}")
return stats
def list_build_artifacts(self) -> str:
"""List build artifacts in dist/ directory.
Returns:
Formatted markdown list of build artifacts
"""
if not self.dist_dir.exists():
return "No build artifacts found (dist/ directory does not exist)"
artifacts = list(self.dist_dir.glob('*'))
if not artifacts:
return "No build artifacts found in dist/"
lines = []
for artifact in sorted(artifacts):
if artifact.is_file():
size = artifact.stat().st_size
size_kb = size / 1024
size_mb = size / (1024 * 1024)
if size_mb >= 1:
size_str = f"{size_mb:.2f} MB"
else:
size_str = f"{size_kb:.2f} KB"
lines.append(f"- **{artifact.name}** ({size_str})")
return '\n'.join(lines) if lines else "No build artifacts found"
def get_validation_results(self) -> str:
"""Get validation results summary.
Returns:
Formatted validation results
"""
# Import here to avoid circular dependency
from ..utils.validation import ReleaseValidator
validator = ReleaseValidator(self.project_root)
is_valid, issues = validator.validate_release_state(force=True) # Force to get all issues
if is_valid:
return "✅ All validation checks passed"
else:
lines = ["Validation Issues:"]
for issue in issues:
lines.append(f"- {issue}")
return '\n'.join(lines)
def _get_project_name(self) -> str:
"""Get project name from pyproject.toml.
Returns:
Project name or default
"""
pyproject_path = self.project_root / 'pyproject.toml'
if not pyproject_path.exists():
return "Project"
try:
import tomllib
except ImportError:
try:
import tomli as tomllib
except ImportError:
return "Project"
try:
with open(pyproject_path, 'rb') as f:
config = tomllib.load(f)
return config.get('project', {}).get('name', 'Project').title()
except Exception:
return "Project"

View File

@@ -4,6 +4,7 @@ Release validation utilities.
This module provides validation functions for release readiness.
"""
import subprocess
from pathlib import Path
from typing import List, Tuple, Optional
@@ -48,6 +49,10 @@ class ReleaseValidator:
config_issues = self._validate_configuration()
issues.extend(config_issues)
# CHANGELOG validation
changelog_issues = self._validate_changelog()
issues.extend(changelog_issues)
return len(issues) == 0, issues
def _validate_git_state(self) -> List[str]:
@@ -186,6 +191,117 @@ class ReleaseValidator:
return len(issues) == 0, issues
def _validate_changelog(self) -> List[str]:
"""Validate CHANGELOG.md using changelog schema.
Returns:
List of CHANGELOG-related issues
"""
issues = []
changelog_path = self.project_root / 'CHANGELOG.md'
# Check if CHANGELOG exists
if not changelog_path.exists():
issues.append("Missing CHANGELOG.md file")
return issues
# Check if changelog schema exists
schema_path = self.project_root / 'markitect' / 'schemas' / 'changelog-schema-v1.0.md'
if not schema_path.exists():
# Schema doesn't exist, skip validation
return issues
# Validate CHANGELOG with schema using markitect validate command
try:
result = subprocess.run(
[
'markitect', 'validate', str(changelog_path),
'--schema', str(schema_path),
'--semantic'
],
capture_output=True,
text=True,
cwd=self.project_root
)
if result.returncode != 0:
issues.append("CHANGELOG.md validation failed against schema")
# Parse output for specific errors
if 'Unreleased section' in result.stdout:
issues.append(" - Missing [Unreleased] section in CHANGELOG")
if 'version format' in result.stdout.lower():
issues.append(" - Invalid version format in CHANGELOG")
except FileNotFoundError:
# markitect command not available
issues.append("Cannot validate CHANGELOG (markitect command not found)")
except Exception as e:
issues.append(f"Error validating CHANGELOG: {e}")
return issues
def validate_changelog_version(self, version: str) -> Tuple[bool, List[str]]:
"""Validate that CHANGELOG has section for specified version.
Args:
version: Version to check (e.g., "0.10.0")
Returns:
Tuple of (is_valid, list_of_issues)
"""
issues = []
changelog_path = self.project_root / 'CHANGELOG.md'
if not changelog_path.exists():
issues.append("CHANGELOG.md not found")
return False, issues
try:
content = changelog_path.read_text()
# Check for version section
version_header = f"## [{version}]"
if version_header not in content:
issues.append(f"CHANGELOG missing section for version {version}")
# Check for Unreleased section
if "## [Unreleased]" not in content:
issues.append("CHANGELOG missing [Unreleased] section")
# Check if version section has a date
import re
date_pattern = rf"## \[{re.escape(version)}\] - \d{{4}}-\d{{2}}-\d{{2}}"
if not re.search(date_pattern, content):
issues.append(f"Version {version} section missing date or has invalid date format")
except Exception as e:
issues.append(f"Error reading CHANGELOG: {e}")
return len(issues) == 0, issues
def check_version_tag_consistency(self, version: str) -> Tuple[bool, List[str]]:
"""Check consistency between CHANGELOG version and git tags.
Args:
version: Version to check (e.g., "0.10.0")
Returns:
Tuple of (is_consistent, list_of_issues)
"""
issues = []
# Check CHANGELOG has the version
changelog_valid, changelog_issues = self.validate_changelog_version(version)
if not changelog_valid:
issues.extend(changelog_issues)
# Check git tag exists
tag_name = version if version.startswith('v') else f'v{version}'
if not self.git_manager.tag_exists(tag_name):
issues.append(f"Git tag {tag_name} doesn't exist for version in CHANGELOG")
return len(issues) == 0, issues
def get_validation_summary(self) -> dict:
"""Get a comprehensive validation summary.
@@ -224,6 +340,10 @@ class ReleaseValidator:
if any('authentication' in issue.lower() for issue in issues):
recommendations.append("Set up authentication tokens for package publishing")
if any('CHANGELOG' in issue for issue in issues):
recommendations.append("Fix CHANGELOG.md format and ensure [Unreleased] section exists")
recommendations.append("Validate with: markitect validate CHANGELOG.md --schema changelog-schema-v1.0.md --semantic")
if not issues:
recommendations.append("Repository is ready for release!")

View File

@@ -1,261 +0,0 @@
# TestDrive-JSUI Capability Makefile
# JavaScript UI testing framework for MarkiTect
# Capability metadata
CAPABILITY_NAME := testdrive-jsui
CAPABILITY_DESCRIPTION := JavaScript UI testing framework with Python integration
# Python virtual environment detection
VENV_PYTHON := $(shell which python3 2>/dev/null || which python 2>/dev/null)
ifeq ($(VENV_PYTHON),)
VENV_PYTHON := python
endif
# Node.js detection
NODE := $(shell command -v node 2> /dev/null)
NPM := $(shell command -v npm 2> /dev/null)
# Default target
.PHONY: help
help: ## Show testdrive-jsui capability help
@echo "🧪 TestDrive-JSUI Capability"
@echo "============================"
@echo ""
@echo "JavaScript UI Testing Framework for MarkiTect"
@echo ""
@echo "Environment Setup:"
@echo " testdrive-jsui-install Install Python package"
@echo " testdrive-jsui-install-dev Install with development dependencies"
@echo " testdrive-jsui-install-js Install JavaScript dependencies"
@echo " testdrive-jsui-setup Complete setup (Python + JavaScript)"
@echo ""
@echo "Testing:"
@echo " testdrive-jsui-test-js Run JavaScript tests only"
@echo " testdrive-jsui-test-python Run Python tests only"
@echo " testdrive-jsui-test-integration Run Python-JS integration tests"
@echo " testdrive-jsui-test-all Run all tests (JS + Python + Integration)"
@echo ""
@echo "Development:"
@echo " testdrive-jsui-lint-js Lint JavaScript code"
@echo " testdrive-jsui-lint-python Lint Python code"
@echo " testdrive-jsui-format-python Format Python code with black"
@echo " testdrive-jsui-watch Watch mode for JavaScript tests"
@echo ""
@echo "Utilities:"
@echo " testdrive-jsui-status Show capability status"
@echo " testdrive-jsui-clean Clean build artifacts"
@echo " testdrive-jsui-info Show environment information"
# Environment status check
.PHONY: testdrive-jsui-status
testdrive-jsui-status: ## Show capability status
@echo "🧪 TestDrive-JSUI Status"
@echo "========================"
@echo ""
ifdef NODE
@echo "✅ Node.js: $(shell node --version)"
else
@echo "❌ Node.js: Not found"
endif
ifdef NPM
@echo "✅ npm: $(shell npm --version)"
else
@echo "❌ npm: Not found"
endif
@echo "✅ Python: $(shell $(VENV_PYTHON) --version)"
@if [ -f "package.json" ]; then \
echo "✅ package.json: Available"; \
else \
echo "❌ package.json: Missing"; \
fi
@if [ -f "pyproject.toml" ]; then \
echo "✅ pyproject.toml: Available"; \
else \
echo "❌ pyproject.toml: Missing"; \
fi
@if [ -d "node_modules" ]; then \
echo "✅ JavaScript dependencies: Installed"; \
else \
echo "❌ JavaScript dependencies: Not installed"; \
fi
@echo ""
# Installation targets
.PHONY: testdrive-jsui-install
testdrive-jsui-install: ## Install Python package
$(VENV_PYTHON) -m pip install -e .
.PHONY: testdrive-jsui-install-dev
testdrive-jsui-install-dev: ## Install with development dependencies
$(VENV_PYTHON) -m pip install -e ".[dev,testing]"
.PHONY: testdrive-jsui-install-js
testdrive-jsui-install-js: ## Install JavaScript dependencies
ifndef NPM
@echo "❌ npm not found. Please install Node.js and npm first."
@exit 1
endif
npm install
.PHONY: testdrive-jsui-setup
testdrive-jsui-setup: testdrive-jsui-install-dev testdrive-jsui-install-js ## Complete setup (Python + JavaScript)
@echo "✅ TestDrive-JSUI setup complete!"
# Testing targets
.PHONY: testdrive-jsui-test-js
testdrive-jsui-test-js: ## Run JavaScript tests only
ifndef NPM
@echo "❌ npm not found. Run 'make testdrive-jsui-install-js' first."
@exit 1
endif
npm test
.PHONY: testdrive-jsui-test-python
testdrive-jsui-test-python: ## Run Python tests only
$(VENV_PYTHON) -m pytest tests/ -v
.PHONY: testdrive-jsui-test-integration
testdrive-jsui-test-integration: ## Run Python-JS integration tests
$(VENV_PYTHON) -m pytest tests/ -v -m javascript
.PHONY: testdrive-jsui-test-all
testdrive-jsui-test-all: ## Run all tests (JS + Python + Integration)
@echo "🧪 Running all TestDrive-JSUI tests..."
@echo ""
ifndef NPM
@echo "❌ npm not found. Run 'make testdrive-jsui-install-js' first."
@exit 1
endif
@echo "📋 JavaScript Tests (Jest):"
@echo "=============================="
@if npm test > /tmp/jest_results.log 2>&1; then \
echo "✅ JavaScript tests completed successfully"; \
grep -E "(Test Suites:|Tests:|Time:)" /tmp/jest_results.log || true; \
else \
echo "❌ JavaScript tests failed"; \
cat /tmp/jest_results.log; \
exit 1; \
fi
@echo ""
@echo "📋 Python Integration Tests (pytest):"
@echo "======================================"
@if $(VENV_PYTHON) -m pytest tests/ -v > /tmp/pytest_results.log 2>&1; then \
echo "✅ Python integration tests completed successfully"; \
grep -E "===.*passed.*===" /tmp/pytest_results.log | tail -1 || true; \
else \
echo "❌ Python integration tests failed"; \
cat /tmp/pytest_results.log; \
exit 1; \
fi
@echo ""
@echo "🎯 Combined Test Results Summary:"
@echo "=================================="
@js_tests=$$(grep "Tests:" /tmp/jest_results.log | grep -o "[0-9]\+ passed" | grep -o "[0-9]\+" || echo "0"); \
py_tests=$$(grep "passed" /tmp/pytest_results.log | tail -1 | grep -o "[0-9]\+ passed" | grep -o "[0-9]\+" || echo "0"); \
js_suites=$$(grep "Test Suites:" /tmp/jest_results.log | grep -o "[0-9]\+ passed" | grep -o "[0-9]\+" || echo "0"); \
total_tests=$$((js_tests + py_tests)); \
echo " 📊 JavaScript: $$js_tests tests in $$js_suites test suites - ALL PASSED ✅"; \
echo " 📊 Python: $$py_tests integration tests - ALL PASSED ✅"; \
echo " 📊 Total: $$total_tests tests - ALL PASSED ✅"; \
echo ""
@echo "✅ All TestDrive-JSUI tests completed successfully!"
@rm -f /tmp/jest_results.log /tmp/pytest_results.log
# Development targets
.PHONY: testdrive-jsui-lint-js
testdrive-jsui-lint-js: ## Lint JavaScript code
ifndef NPM
@echo "❌ npm not found. Run 'make testdrive-jsui-install-js' first."
@exit 1
endif
npm run lint
.PHONY: testdrive-jsui-lint-python
testdrive-jsui-lint-python: ## Lint Python code
$(VENV_PYTHON) -m flake8 src/ tests/
.PHONY: testdrive-jsui-format-python
testdrive-jsui-format-python: ## Format Python code with black
$(VENV_PYTHON) -m black src/ tests/
.PHONY: testdrive-jsui-watch
testdrive-jsui-watch: ## Watch mode for JavaScript tests
ifndef NPM
@echo "❌ npm not found. Run 'make testdrive-jsui-install-js' first."
@exit 1
endif
npm run test:watch
# Utility targets
.PHONY: testdrive-jsui-clean
testdrive-jsui-clean: ## Clean build artifacts
rm -rf build/
rm -rf dist/
rm -rf *.egg-info/
rm -rf .pytest_cache/
rm -rf coverage/
rm -rf .coverage
rm -rf node_modules/.cache/
find . -type d -name __pycache__ -exec rm -rf {} +
find . -type f -name "*.pyc" -delete
.PHONY: testdrive-jsui-info
testdrive-jsui-info: ## Show environment information
@echo "🧪 TestDrive-JSUI Environment Information"
@echo "========================================="
@echo ""
@echo "📁 Capability Root: $(shell pwd)"
@echo "🐍 Python: $(VENV_PYTHON)"
@echo "📦 Python Version: $(shell $(VENV_PYTHON) --version)"
ifdef NODE
@echo "🟢 Node.js: $(shell node --version)"
@echo "📦 npm: $(shell npm --version)"
else
@echo "❌ Node.js: Not available"
endif
@echo ""
@echo "📋 Available JavaScript Tests:"
@if [ -d "js/tests" ]; then \
find js/tests -name "*.js" -type f | sed 's/^/ - /'; \
else \
echo " No JavaScript tests found"; \
fi
@echo ""
@echo "📋 Available Python Tests:"
@if [ -d "tests" ]; then \
find tests -name "test_*.py" -type f | sed 's/^/ - /'; \
else \
echo " No Python tests found"; \
fi
# Integration with main capability system
.PHONY: capability-info
capability-info: ## Show capability information
@echo "Name: $(CAPABILITY_NAME)"
@echo "Description: $(CAPABILITY_DESCRIPTION)"
@echo "Type: JavaScript Testing Framework"
@echo "Status: Development"
@echo "Targets:"
@$(MAKE) --no-print-directory help | grep "^ " | sed 's/^ / /'
# Quick start target
.PHONY: testdrive-jsui-quickstart
testdrive-jsui-quickstart: ## Quick start: setup and run basic tests
@echo "🚀 TestDrive-JSUI Quick Start"
@echo "============================="
@echo ""
@echo "📋 Step 1: Installing dependencies..."
@$(MAKE) --no-print-directory testdrive-jsui-setup
@echo ""
@echo "📋 Step 2: Running status check..."
@$(MAKE) --no-print-directory testdrive-jsui-status
@echo ""
@echo "📋 Step 3: Running basic tests..."
@$(MAKE) --no-print-directory testdrive-jsui-test-python
@echo ""
@echo "✅ Quick start complete! Use 'make testdrive-jsui-help' for more options."
# Standard test target for capability discovery system compatibility
.PHONY: test
test: ## Run all tests (required for capability integration)
@$(MAKE) --no-print-directory testdrive-jsui-test-all

View File

@@ -1,313 +0,0 @@
# TestDrive-JSUI Capability
A comprehensive JavaScript UI testing framework capability for MarkiTect. Provides seamless integration between Python and JavaScript testing environments, enabling safe development and testing of JavaScript UI components.
## 🎯 **Purpose**
TestDrive-JSUI is designed to:
- **🔒 Protect existing JavaScript UI functionality** during refactoring and development
- **🧪 Integrate JavaScript tests** into the main Python test suite
- **🏗️ Provide a clean architecture** for JavaScript framework development
- **📊 Enable comprehensive testing** of JavaScript UI components
- **🚀 Support future extensibility** for JavaScript framework evolution
## 🏗️ **Architecture**
```
testdrive-jsui/
├── src/testdrive_jsui/ # Python package
│ ├── core/ # Core framework components
│ ├── components/ # UI component helpers
│ ├── utils/ # Utility functions
│ └── testing/ # Python-JS bridge
│ ├── js_test_runner.py # JavaScript test execution
│ └── integration.py # Pytest integration
├── js/ # JavaScript source
│ ├── core/ # Core JS components
│ ├── components/ # UI components
│ ├── utils/ # JS utilities
│ └── tests/ # JavaScript tests
├── tests/ # Python tests
├── Makefile # Capability commands
├── pyproject.toml # Python package config
├── package.json # JavaScript dependencies
└── README.md # This file
```
## 🚀 **Quick Start**
### Prerequisites
- **Python 3.8+** with pip
- **Node.js 16+** with npm
- **MarkiTect main project** installed
### Installation
```bash
# Navigate to the capability directory
cd capabilities/testdrive-jsui
# Quick setup (installs everything)
make testdrive-jsui-quickstart
# Or step by step:
make testdrive-jsui-setup # Install all dependencies
make testdrive-jsui-status # Check environment
make testdrive-jsui-test-all # Run all tests
```
## 🧪 **Testing**
### JavaScript Tests
```bash
# Run JavaScript tests only
make testdrive-jsui-test-js
# Run with coverage
npm run test:coverage
# Watch mode for development
make testdrive-jsui-watch
```
### Python Integration Tests
```bash
# Run Python tests only
make testdrive-jsui-test-python
# Run integration tests
make testdrive-jsui-test-integration
# Run all tests
make testdrive-jsui-test-all
```
### Main Project Integration
From the main MarkiTect project:
```bash
# Run capability tests
make testdrive-jsui-test-all
# Include in main test suite
make test-all # (when integrated)
```
## 📋 **Available Commands**
| Command | Description |
|---------|-------------|
| `make testdrive-jsui-help` | Show all available commands |
| `make testdrive-jsui-status` | Check environment status |
| `make testdrive-jsui-setup` | Install all dependencies |
| `make testdrive-jsui-test-all` | Run all tests |
| `make testdrive-jsui-watch` | Development watch mode |
| `make testdrive-jsui-clean` | Clean build artifacts |
## 🔧 **Development**
### Adding JavaScript Tests
1. Create test files in `js/tests/`:
```javascript
// js/tests/test-my-component.js
describe('MyComponent', () => {
test('should do something', () => {
// Your test here
});
});
```
2. Run tests:
```bash
make testdrive-jsui-test-js
```
### Adding Python Integration Tests
1. Create test files in `tests/`:
```python
# tests/test_my_integration.py
import pytest
from testdrive_jsui.testing import JavaScriptTestRunner
@pytest.mark.javascript
def test_my_js_component():
runner = JavaScriptTestRunner()
result = runner.run_specific_test('test-my-component.js')
assert result.success
```
2. Run tests:
```bash
make testdrive-jsui-test-integration
```
### Code Quality
```bash
# Lint JavaScript
make testdrive-jsui-lint-js
# Lint Python
make testdrive-jsui-lint-python
# Format Python code
make testdrive-jsui-format-python
```
## 🔗 **Integration with Main Project**
### Python Test Suite Integration
The capability provides pytest integration to run JavaScript tests from Python:
```python
# In main project tests
from testdrive_jsui.testing import JavaScriptTestRunner
def test_javascript_ui_components():
runner = JavaScriptTestRunner()
result = runner.run_js_tests()
assert result.success
assert result.tests_passed > 0
```
### Capability System Integration
The capability integrates with MarkiTect's capability discovery system:
```bash
# From main project
make capabilities-list # Shows testdrive-jsui
make testdrive-jsui-test-all # Direct delegation
make capabilities-test # Includes JS tests
```
## 📊 **Testing Framework Features**
### JavaScript Test Runner
- **Jest integration** with JSDOM environment
- **Coverage reporting** with detailed metrics
- **Test isolation** with proper setup/teardown
- **Mock support** for DOM APIs and browser features
- **Async testing** support for modern JavaScript
### Python-JavaScript Bridge
- **Subprocess execution** of JavaScript tests
- **Result parsing** with structured output
- **Error handling** with detailed failure information
- **Test discovery** for pytest integration
- **Coverage integration** between Python and JavaScript
### Safety Mechanisms
- **Copy-first migration** (never move, always copy)
- **Dual-track testing** during migration
- **Gradual integration** with rollback options
- **Test verification** at each step
- **Environment validation** before execution
## 🔄 **Migration Strategy**
When migrating JavaScript UI code to this capability:
1. **Copy** (don't move) JavaScript files to `js/` directory
2. **Verify** tests work in new location
3. **Create** Python integration tests
4. **Run** dual-track testing to compare results
5. **Gradually** switch to capability-based testing
6. **Remove** original files only after full verification
## 📚 **Examples**
### Running Specific Tests
```bash
# Run a specific JavaScript test
npm test -- --testNamePattern="SectionManager"
# Run specific Python integration test
pytest tests/test_section_manager_integration.py -v
# Run tests with coverage
npm run test:coverage
```
### Environment Information
```bash
# Get detailed environment info
make testdrive-jsui-info
# Check what tests are available
make testdrive-jsui-status
```
### Development Workflow
```bash
# Start development session
make testdrive-jsui-watch # Terminal 1: Watch JS tests
make testdrive-jsui-test-python # Terminal 2: Run Python tests
# Before committing
make testdrive-jsui-lint-js # Lint JavaScript
make testdrive-jsui-format-python # Format Python
make testdrive-jsui-test-all # Run all tests
```
## 🎯 **Future Enhancements**
- **Visual regression testing** with screenshot comparison
- **Performance benchmarking** for JavaScript components
- **Browser automation** with Selenium/Playwright
- **Component documentation** auto-generation
- **Real browser testing** in CI/CD pipelines
## 📋 **Troubleshooting**
### Common Issues
**Node.js not found:**
```bash
# Install Node.js first
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs
```
**Tests failing:**
```bash
# Check environment
make testdrive-jsui-status
# Reinstall dependencies
make testdrive-jsui-clean
make testdrive-jsui-setup
```
**Integration issues:**
```bash
# Verify Python package is installed
pip list | grep testdrive-jsui
# Check JavaScript dependencies
npm list
```
## 📄 **License**
MIT License - See main MarkiTect project for details.
---
*Generated: 2025-11-09*
*Status: Phase 1 Implementation*
*Next: Copy JavaScript files and create integration tests*

View File

@@ -1,191 +0,0 @@
/**
* DebugPanel Component
*
* Extracted from monolithic editor.js as part of architecture refactoring.
* Handles debug message display and management for client-side debugging.
*
* Dependencies:
* - None (standalone component)
*/
/**
* DebugPanel - Manages debug message display and interaction
*/
class DebugPanel {
constructor() {
this.messages = [];
this.isActive = false;
this.maxMessages = 1000; // Keep last 1000 messages
}
/**
* Add a debug message
*/
addMessage(message, category = 'INFO') {
const messageObj = {
message,
category,
timestamp: new Date().toLocaleTimeString()
};
this.messages.push(messageObj);
// Keep only last maxMessages
if (this.messages.length > this.maxMessages) {
this.messages = this.messages.slice(-this.maxMessages);
}
// Auto-update if panel is visible
if (this.isActive) {
this.update();
}
}
/**
* Toggle the debug panel on/off
*/
toggle() {
const debugContainer = document.getElementById('debug-messages-container');
const debugButton = document.getElementById('toggle-debug');
if (!debugContainer || !debugButton) {
console.warn('DebugPanel: Required DOM elements not found');
return;
}
if (this.isActive) {
this.hide();
} else {
this.show();
}
}
/**
* Show the debug panel
*/
show() {
const debugContainer = document.getElementById('debug-messages-container');
const debugButton = document.getElementById('toggle-debug');
if (!debugContainer || !debugButton) {
console.warn('DebugPanel: Required DOM elements not found');
return;
}
debugContainer.style.display = 'block';
debugButton.textContent = '🔍 Debug (ON)';
debugButton.style.background = '#28a745';
this.isActive = true;
this.update();
}
/**
* Hide the debug panel
*/
hide() {
const debugContainer = document.getElementById('debug-messages-container');
const debugButton = document.getElementById('toggle-debug');
if (!debugContainer || !debugButton) {
console.warn('DebugPanel: Required DOM elements not found');
return;
}
debugContainer.style.display = 'none';
debugButton.textContent = '🔍 Debug';
debugButton.style.background = '#6c757d';
this.isActive = false;
}
/**
* Update the debug panel with current messages
*/
update() {
const debugContainer = document.getElementById('debug-messages-container');
if (!debugContainer || !this.isActive) {
return;
}
if (this.messages.length === 0) {
debugContainer.innerHTML = '<div style="color: #6c757d; font-style: italic; padding: 12px;">No debug messages yet. Click sections to generate debug output.</div>';
return;
}
// Show the last 50 messages in reverse order (newest first)
const recentMessages = this.messages.slice(-50).reverse();
const messagesHtml = recentMessages.map(msg => {
const categoryColor = {
'INFO': '#17a2b8',
'WARNING': '#ffc107',
'ERROR': '#dc3545',
'SUCCESS': '#28a745',
'DEBUG': '#6f42c1'
}[msg.category] || '#6c757d';
return `
<div style="margin-bottom: 6px; padding: 4px; border-left: 3px solid ${categoryColor}; background: white; border-radius: 2px;">
<span style="color: #6c757d; font-size: 11px;">[${msg.timestamp}]</span>
<span style="color: ${categoryColor}; font-weight: bold;">${msg.category}:</span>
<span style="color: #333;">${msg.message}</span>
</div>
`;
}).join('');
debugContainer.innerHTML = `
<div style="margin-bottom: 8px; padding: 6px; background: #e9ecef; border-radius: 4px; font-weight: bold; color: #495057;">
Debug Messages (${this.messages.length} total, showing last ${recentMessages.length})
<button id="debug-clear-btn" style="float: right; background: #dc3545; color: white; border: none; padding: 2px 6px; border-radius: 2px; font-size: 11px; cursor: pointer;">Clear</button>
</div>
<div style="max-height: 250px; overflow-y: auto;">
${messagesHtml}
</div>
`;
// Add event listener for clear button
const clearBtn = debugContainer.querySelector('#debug-clear-btn');
if (clearBtn) {
clearBtn.addEventListener('click', () => {
this.clear();
});
}
// Auto-scroll to bottom to show newest messages
const scrollContainer = debugContainer.querySelector('div[style*="overflow-y"]');
if (scrollContainer) {
scrollContainer.scrollTop = scrollContainer.scrollHeight;
}
}
/**
* Clear all debug messages
*/
clear() {
this.messages = [];
this.update();
}
/**
* Get the number of messages
*/
getMessageCount() {
return this.messages.length;
}
/**
* Get recent messages
*/
getRecentMessages(count = 10) {
return this.messages.slice(-count);
}
}
// Export for use in tests and other modules
if (typeof module !== 'undefined' && module.exports) {
module.exports = { DebugPanel };
}
// Export for browser use
if (typeof window !== 'undefined') {
window.DebugPanel = DebugPanel;
}

View File

@@ -1,279 +0,0 @@
/**
* DocumentControls Component
*
* Extracted from monolithic editor.js as part of architecture refactoring.
* Handles the floating control panel and document-level actions.
*
* Dependencies:
* - None (standalone component)
*/
/**
* DocumentControls - Manages the floating control panel and its buttons
*/
class DocumentControls {
constructor() {
this.controlPanel = null;
this.buttons = new Map();
this.eventHandlers = new Map();
this.isVisible = true;
}
/**
* Create the control panel and add it to the DOM
*/
create() {
if (this.controlPanel) {
this.destroy(); // Remove existing panel
}
// Also remove any existing panel with the same ID in the DOM
const existingPanel = document.getElementById('markitect-global-controls');
if (existingPanel && existingPanel.parentNode) {
existingPanel.parentNode.removeChild(existingPanel);
}
// Create the floating control panel
this.controlPanel = document.createElement('div');
this.controlPanel.id = 'markitect-global-controls';
this.controlPanel.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: rgba(248, 249, 250, 0.95);
border: 1px solid #dee2e6;
border-radius: 8px;
padding: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 1000;
backdrop-filter: blur(8px);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
font-size: 14px;
min-width: 200px;
`;
// Add title
const title = document.createElement('div');
title.style.cssText = `
font-weight: 600;
margin-bottom: 8px;
color: #495057;
border-bottom: 1px solid #dee2e6;
padding-bottom: 4px;
`;
title.textContent = 'Document Controls';
// Create button container
const buttonContainer = document.createElement('div');
buttonContainer.id = 'button-container';
buttonContainer.style.cssText = `
display: flex;
flex-direction: column;
gap: 6px;
`;
this.controlPanel.appendChild(title);
this.controlPanel.appendChild(buttonContainer);
// Add default buttons
this.addDefaultButtons();
// Add debug messages container
this.addDebugContainer();
// Add to DOM
document.body.appendChild(this.controlPanel);
}
/**
* Add default buttons to the control panel
*/
addDefaultButtons() {
// Save Document button
this.addButton('save-document', '💾 Save Document', '#28a745');
// Reset All button
this.addButton('reset-all', '🔄 Reset All', '#ffc107', '#212529');
// Show Status button
this.addButton('show-status', '📊 Show Status', '#17a2b8');
// Debug button
this.addButton('toggle-debug', '🔍 Debug', '#6c757d');
}
/**
* Add debug container to the control panel
*/
addDebugContainer() {
const debugContainer = document.createElement('div');
debugContainer.id = 'debug-messages-container';
debugContainer.style.cssText = `
margin-top: 12px;
max-height: 300px;
overflow-y: auto;
border: 1px solid #dee2e6;
border-radius: 4px;
background: #f8f9fa;
padding: 8px;
font-family: 'Courier New', monospace;
font-size: 12px;
line-height: 1.4;
display: none;
`;
this.controlPanel.appendChild(debugContainer);
}
/**
* Add a button to the control panel
*/
addButton(id, text, backgroundColor, textColor = 'white') {
const buttonContainer = this.controlPanel.querySelector('#button-container');
if (!buttonContainer) {
throw new Error('Button container not found. Call create() first.');
}
const button = document.createElement('button');
button.id = id;
button.textContent = text;
button.style.cssText = `
background: ${backgroundColor};
color: ${textColor};
border: none;
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
transition: background-color 0.2s;
`;
buttonContainer.appendChild(button);
this.buttons.set(id, button);
return button;
}
/**
* Remove a button from the control panel
*/
removeButton(id) {
const button = this.buttons.get(id);
if (button && button.parentNode) {
button.parentNode.removeChild(button);
this.buttons.delete(id);
this.eventHandlers.delete(id);
}
}
/**
* Set event handlers for buttons
*/
setEventHandlers(handlers) {
for (const [buttonId, handler] of Object.entries(handlers)) {
const button = this.buttons.get(buttonId);
if (button) {
// Remove existing handler if any
if (this.eventHandlers.has(buttonId)) {
button.removeEventListener('click', this.eventHandlers.get(buttonId));
}
// Add new handler
button.addEventListener('click', handler);
this.eventHandlers.set(buttonId, handler);
}
}
}
/**
* Show the control panel
*/
show() {
if (this.controlPanel) {
this.controlPanel.style.display = 'block';
this.isVisible = true;
}
}
/**
* Hide the control panel
*/
hide() {
if (this.controlPanel) {
this.controlPanel.style.display = 'none';
this.isVisible = false;
}
}
/**
* Update status display (can be extended as needed)
*/
updateStatus(status) {
// This method can be extended to show status information
// For now, it just stores the status for potential display
this.lastStatus = status;
// Could update a status indicator in the panel if needed
if (status && this.controlPanel) {
const title = this.controlPanel.querySelector('div');
if (title) {
const statusText = `Document Controls (${status.totalSections} sections, ${status.editingSections} editing)`;
// Could update title or add status indicator
}
}
}
/**
* Get the control panel element
*/
getControlPanel() {
return this.controlPanel;
}
/**
* Destroy the control panel and clean up
*/
destroy() {
if (this.controlPanel && this.controlPanel.parentNode) {
this.controlPanel.parentNode.removeChild(this.controlPanel);
}
// Clean up references
this.controlPanel = null;
this.buttons.clear();
this.eventHandlers.clear();
this.isVisible = true;
}
/**
* Check if the control panel is visible
*/
isVisible() {
return this.isVisible && this.controlPanel && this.controlPanel.style.display !== 'none';
}
/**
* Get all button IDs
*/
getButtonIds() {
return Array.from(this.buttons.keys());
}
/**
* Get a specific button by ID
*/
getButton(id) {
return this.buttons.get(id);
}
}
// Export for use in tests and other modules
if (typeof module !== 'undefined' && module.exports) {
module.exports = { DocumentControls };
}
// Export for browser use
if (typeof window !== 'undefined') {
window.DocumentControls = DocumentControls;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,544 +0,0 @@
/**
* SectionManager Component
*
* Extracted from monolithic editor.js as part of architecture refactoring.
* Manages the collection of sections and their state transitions.
*
* Dependencies:
* - EditState enum (imported)
* - SectionType enum (imported)
* - Section class (imported)
* - debug function (imported)
*/
// Import dependencies - these will be separate modules
const EditState = Object.freeze({
ORIGINAL: 'original',
EDITING: 'editing',
MODIFIED: 'modified',
SAVED: 'saved'
});
const SectionType = Object.freeze({
HEADING: 'heading',
PARAGRAPH: 'paragraph',
LIST: 'list',
CODE: 'code',
QUOTE: 'quote',
TABLE: 'table',
HR: 'hr',
IMAGE: 'image'
});
// Debug function (will be extracted to utils)
function debug(message, category = 'INFO') {
// Simple console debug for now - will be enhanced later
console.log(`DEBUG ${category}: ${message}`);
}
/**
* Section Class - manages individual section state and content
*/
class Section {
constructor(id, markdown, type) {
this.id = id;
this.originalMarkdown = markdown;
this.currentMarkdown = markdown;
this.editingMarkdown = markdown;
this.pendingMarkdown = null;
this.type = type;
this.state = EditState.ORIGINAL;
this.domElement = null;
this.lastSaved = null;
this.created = new Date();
}
static generateId(markdown, position, strategy = 'hash', parentId = null) {
return this.generateIdWithStrategy(markdown, position, strategy, parentId);
}
static generateIdWithStrategy(markdown, position, strategy = 'hash', parentId = null) {
const sanitizedContent = this.sanitizeContentForId(markdown);
const normalizedContent = this.normalizeContentForHashing(sanitizedContent);
const sectionType = this.detectType(markdown);
switch (strategy) {
case 'timestamp':
return this.generateTimestampId(normalizedContent, position, sectionType);
case 'sequential':
return this.generateSequentialId(normalizedContent, position, sectionType);
case 'hierarchical':
return this.generateHierarchicalId(normalizedContent, position, parentId);
case 'hash':
default:
return this.generateAdvancedId(normalizedContent, position, sectionType);
}
}
static generateAdvancedId(content, position, sectionType) {
const contentHash = this.generateCryptoHash(content);
const safeType = sectionType || 'paragraph';
const typePrefix = safeType.substring(0, 3);
const positionHex = position.toString(16).padStart(2, '0');
return `section-${typePrefix}-${contentHash}-${positionHex}`;
}
static generateCryptoHash(content) {
let hash = 0;
if (content.length === 0) return '00000000';
for (let i = 0; i < content.length; i++) {
const char = content.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
const hexHash = Math.abs(hash).toString(16).padStart(8, '0');
return hexHash.substring(0, 8);
}
static normalizeContentForHashing(content) {
if (!content || typeof content !== 'string') {
return '';
}
return content
.trim()
.replace(/\s+/g, ' ')
.replace(/\r\n/g, '\n')
.toLowerCase();
}
static sanitizeContentForId(content) {
if (!content || typeof content !== 'string') {
return '';
}
return content
.replace(/<[^>]*>/g, '')
.replace(/javascript:/gi, '')
.replace(/[^\w\s\-_.#]/g, '')
.trim();
}
static generateTimestampId(content, position = 0, sectionType = 'paragraph') {
const timestamp = Date.now().toString(36);
const contentSnippet = this.generateCryptoHash(content || '').substring(0, 4);
const safeType = sectionType || 'paragraph';
const typePrefix = safeType.substring(0, 3);
return `section-${typePrefix}-${contentSnippet}-${timestamp}`;
}
static generateSequentialId(content, position, sectionType = 'paragraph') {
const safeType = sectionType || 'paragraph';
const typePrefix = safeType.substring(0, 3);
const seqNumber = (position || 0).toString().padStart(3, '0');
const contentHash = this.generateCryptoHash(content || '').substring(0, 4);
return `section-${typePrefix}-seq${seqNumber}-${contentHash}`;
}
static generateHierarchicalId(content, position, parentId = null) {
const contentHash = this.generateCryptoHash(content || '').substring(0, 6);
if (parentId) {
const childIndex = (position || 0).toString().padStart(2, '0');
return `${parentId}-child-${childIndex}-${contentHash}`;
} else {
return `section-root-${position || 0}-${contentHash}`;
}
}
static detectType(markdown) {
if (!markdown || typeof markdown !== 'string') {
return SectionType.PARAGRAPH;
}
const content = markdown.replace(/^\n+|\n+$/g, '');
if (!content) {
return SectionType.PARAGRAPH;
}
const trimmed = content.trim();
// Detection order matters - most specific first
if (this.isHeading(trimmed)) {
return SectionType.HEADING;
}
if (this.isImage(trimmed)) {
return SectionType.IMAGE;
}
if (this.isCodeBlock(trimmed)) {
return SectionType.CODE;
}
return SectionType.PARAGRAPH;
}
static isHeading(trimmed) {
const headingPattern = /^#{1,6}\s+.+/;
return headingPattern.test(trimmed);
}
static isImage(trimmed) {
const imagePattern = /!\[.*?\]\([^)]+\)/;
return imagePattern.test(trimmed);
}
static isCodeBlock(trimmed) {
if (trimmed.startsWith('```') || trimmed.startsWith('~~~')) {
return true;
}
if (trimmed.includes('```') || trimmed.includes('~~~')) {
const codeBlockPattern = /```[\s\S]*?```|~~~[\s\S]*?~~~/;
if (codeBlockPattern.test(trimmed)) {
return true;
}
}
return false;
}
startEdit() {
if (this.state === EditState.EDITING) {
throw new Error(`Section ${this.id} is already being edited`);
}
this.editingMarkdown = this.pendingMarkdown || this.currentMarkdown;
this.state = EditState.EDITING;
return this.editingMarkdown;
}
updateContent(markdown) {
if (this.state !== EditState.EDITING) {
throw new Error(`Section ${this.id} is not in editing state`);
}
this.editingMarkdown = markdown;
}
acceptChanges() {
if (this.state !== EditState.EDITING) {
throw new Error(`Section ${this.id} is not in editing state`);
}
this.currentMarkdown = this.editingMarkdown;
this.editingMarkdown = null;
this.pendingMarkdown = null;
this.state = EditState.SAVED;
this.lastSaved = new Date();
return this.currentMarkdown;
}
cancelChanges() {
if (this.state !== EditState.EDITING) {
throw new Error(`Section ${this.id} is not in editing state`);
}
this.editingMarkdown = null;
if (this.pendingMarkdown !== null) {
this.state = EditState.MODIFIED;
return this.pendingMarkdown;
} else if (this.lastSaved !== null) {
this.state = EditState.SAVED;
return this.currentMarkdown;
} else {
this.state = this.hasChanges() ? EditState.MODIFIED : EditState.ORIGINAL;
return this.currentMarkdown;
}
}
stopEditing() {
if (this.state !== EditState.EDITING) {
return this.state;
}
if (this.editingMarkdown && this.editingMarkdown !== this.currentMarkdown) {
this.pendingMarkdown = this.editingMarkdown;
this.state = EditState.MODIFIED;
} else {
this.pendingMarkdown = null;
if (this.lastSaved !== null) {
this.state = EditState.SAVED;
} else {
this.state = this.hasChanges() ? EditState.MODIFIED : EditState.ORIGINAL;
}
}
this.editingMarkdown = null;
return this.state;
}
resetToOriginal() {
this.currentMarkdown = this.originalMarkdown;
this.editingMarkdown = this.originalMarkdown;
this.pendingMarkdown = null;
this.state = EditState.ORIGINAL;
return this.originalMarkdown;
}
isEditing() {
return this.state === EditState.EDITING;
}
hasChanges() {
return this.currentMarkdown !== this.originalMarkdown;
}
getStatus() {
return {
id: this.id,
state: this.state,
hasChanges: this.hasChanges(),
isEditing: this.isEditing(),
contentLength: this.currentMarkdown.length,
lastSaved: this.lastSaved,
type: this.type,
originalLength: this.originalMarkdown.length,
currentLength: this.currentMarkdown.length
};
}
isImage() {
return this.type === SectionType.IMAGE;
}
redetectType(content = null) {
const markdown = content || this.currentMarkdown;
const oldType = this.type;
this.type = Section.detectType(markdown);
if (oldType !== this.type) {
debug(`Section ${this.id} type changed from ${oldType} to ${this.type}`, 'TYPE_DETECTION');
}
return this.type;
}
}
/**
* SectionManager - Manages the collection of sections
*/
class SectionManager {
constructor() {
this.sections = new Map();
this.listeners = new Map();
this.statusInterval = null;
this.lastStatusUpdate = new Date().toISOString();
}
on(event, callback) {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event).push(callback);
}
emit(event, data) {
if (this.listeners.has(event)) {
this.listeners.get(event).forEach(callback => callback(data));
}
}
createSectionsFromMarkdown(markdownContent) {
// Split content into blocks separated by double newlines
const blocks = markdownContent.split(/\n\s*\n/);
const sections = [];
let position = 0;
for (const block of blocks) {
const trimmedBlock = block.trim();
if (!trimmedBlock) continue;
// Check if this block should be split further
const lines = trimmedBlock.split('\n');
let currentSection = '';
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const isHeading = /^#{1,6}\s/.test(line.trim());
const isImage = /^\s*!\[.*?\]\(.*?\)\s*$/.test(line);
// Each heading or image starts a new section
if ((isHeading || isImage) && currentSection.trim()) {
// Save the previous section
const sectionId = Section.generateId(currentSection, position);
const sectionType = Section.detectType(currentSection);
const section = new Section(sectionId, currentSection.trim(), sectionType);
sections.push(section);
this.sections.set(sectionId, section);
position++;
currentSection = line;
} else {
if (currentSection) currentSection += '\n';
currentSection += line;
}
}
// Save the final section from this block
if (currentSection.trim()) {
const sectionId = Section.generateId(currentSection, position);
const sectionType = Section.detectType(currentSection);
const section = new Section(sectionId, currentSection.trim(), sectionType);
sections.push(section);
this.sections.set(sectionId, section);
position++;
}
}
this.emit('sections-created', { sections, count: sections.length });
return sections;
}
startEditing(sectionId) {
debug('MANAGER: startEditing called for: ' + sectionId, 'MANAGER');
const section = this.sections.get(sectionId);
if (!section) {
throw new Error(`Section ${sectionId} not found`);
}
if (section.isEditing()) {
debug('MANAGER: Section already in editing state: ' + sectionId, 'MANAGER');
return section.editingMarkdown;
}
debug('MANAGER: Starting edit for section: ' + sectionId, 'MANAGER');
const content = section.startEdit();
debug('MANAGER: About to emit edit-started event for: ' + sectionId, 'MANAGER');
this.emit('edit-started', { sectionId, content, section: section.getStatus() });
debug('MANAGER: Emitted edit-started event for: ' + sectionId, 'MANAGER');
return content;
}
updateContent(sectionId, markdown) {
const section = this.sections.get(sectionId);
if (!section) {
throw new Error(`Section ${sectionId} not found`);
}
const oldType = section.type;
section.updateContent(markdown);
const newType = section.redetectType(markdown);
const eventData = {
sectionId,
markdown,
section: section.getStatus(),
typeChanged: oldType !== newType,
oldType,
newType
};
this.emit('content-updated', eventData);
if (oldType !== newType) {
this.emit('section-type-changed', {
sectionId,
oldType,
newType,
section: section.getStatus()
});
}
}
acceptChanges(sectionId) {
const section = this.sections.get(sectionId);
if (!section) {
throw new Error(`Section ${sectionId} not found`);
}
const content = section.acceptChanges();
this.emit('changes-accepted', { sectionId, content, section: section.getStatus() });
return content;
}
cancelChanges(sectionId) {
const section = this.sections.get(sectionId);
if (!section) {
throw new Error(`Section ${sectionId} not found`);
}
const content = section.cancelChanges();
this.emit('changes-cancelled', { sectionId, content, section: section.getStatus() });
return content;
}
resetSection(sectionId) {
const section = this.sections.get(sectionId);
if (!section) {
throw new Error(`Section ${sectionId} not found`);
}
const content = section.resetToOriginal();
this.emit('section-reset', { sectionId, content, section: section.getStatus() });
return content;
}
getDocumentMarkdown() {
const sortedSections = Array.from(this.sections.values())
.sort((a, b) => a.created - b.created);
return sortedSections.map(section => section.currentMarkdown).join('\n\n');
}
getAllSections() {
return Array.from(this.sections.values());
}
getDocumentStatus() {
const sections = Array.from(this.sections.values());
const editingSections = sections.filter(section => section.isEditing).length;
return {
totalSections: sections.length,
editingSections: editingSections
};
}
extractHeadings(content) {
if (!content) return [];
const lines = content.split('\n');
return lines.filter(line => /^#{1,6}\s/.test(line.trim()));
}
handleSectionSplit(sectionId, newContent) {
const section = this.sections.get(sectionId);
if (!section) {
throw new Error(`Section ${sectionId} not found`);
}
// Remove the original section
this.sections.delete(sectionId);
// Create new sections from the content
const newSections = this.createSectionsFromMarkdown(newContent);
// Emit section-split event
this.emit('section-split', {
originalSectionId: sectionId,
newSections: newSections,
count: newSections.length
});
return newSections;
}
createSectionsFromContent(content) {
return this.createSectionsFromMarkdown(content);
}
}
// Export for use in tests and other modules
if (typeof module !== 'undefined' && module.exports) {
module.exports = { SectionManager, Section, EditState, SectionType };
}
// Export for browser use
if (typeof window !== 'undefined') {
window.SectionManager = SectionManager;
window.Section = Section;
window.EditState = EditState;
window.SectionType = SectionType;
}

View File

@@ -1,349 +0,0 @@
/**
* Button Functionality and DOM Events Tests
*
* Tests button interactions, event handling, and DOM manipulation
* Based on functionality from history/javascript-dev-tests/test_*button*.js and test_*events*.js files
*/
describe('Button Functionality and DOM Events', () => {
let mockSection;
let documentControls;
beforeEach(() => {
// Setup DOM with various buttons and controls
document.body.innerHTML = `
<div id="content">
<div class="section" data-section-id="test-section">
<div class="section-content">
<p>Section content</p>
</div>
<div class="section-controls">
<button class="edit-btn" data-action="edit">Edit</button>
<button class="accept-btn" data-action="accept" style="display: none;">Accept</button>
<button class="cancel-btn" data-action="cancel" style="display: none;">Cancel</button>
<button class="delete-btn" data-action="delete">Delete</button>
</div>
</div>
<div class="floating-controls">
<button class="add-section-btn">Add Section</button>
<button class="save-all-btn">Save All</button>
</div>
</div>
`;
mockSection = document.querySelector('.section');
// Load components
require('../components/document-controls.js');
if (global.DocumentControls) {
documentControls = new global.DocumentControls(document.getElementById('content'));
}
});
afterEach(() => {
document.body.innerHTML = '';
jest.clearAllMocks();
});
describe('Section edit buttons', () => {
test('should show accept/cancel buttons when edit is clicked', () => {
const editBtn = document.querySelector('.edit-btn');
const acceptBtn = document.querySelector('.accept-btn');
const cancelBtn = document.querySelector('.cancel-btn');
expect(editBtn).toBeTruthy();
// Simulate edit button click
editBtn.click();
// In real implementation, accept/cancel should become visible
expect(acceptBtn.style.display).toBe('none'); // Initially hidden
expect(cancelBtn.style.display).toBe('none'); // Initially hidden
// Test that buttons exist for functionality
expect(acceptBtn).toBeTruthy();
expect(cancelBtn).toBeTruthy();
});
test('should hide edit button when in edit mode', () => {
const editBtn = document.querySelector('.edit-btn');
editBtn.click();
// In real implementation, edit button should be hidden
expect(editBtn.style.display).not.toBe('block');
});
test('should restore edit button when edit is cancelled', () => {
const editBtn = document.querySelector('.edit-btn');
const cancelBtn = document.querySelector('.cancel-btn');
// Simulate edit mode
editBtn.style.display = 'none';
cancelBtn.style.display = 'inline-block';
cancelBtn.click();
// In real implementation, should restore edit button
expect(cancelBtn).toBeTruthy();
expect(editBtn).toBeTruthy();
});
});
describe('Button event propagation', () => {
test('should prevent event bubbling for section buttons', () => {
const editBtn = document.querySelector('.edit-btn');
let sectionClicked = false;
mockSection.addEventListener('click', () => {
sectionClicked = true;
});
// Create event with stopPropagation mock
const clickEvent = new Event('click', { bubbles: true });
clickEvent.stopPropagation = jest.fn();
editBtn.dispatchEvent(clickEvent);
// In real implementation, should call stopPropagation
expect(clickEvent.stopPropagation).toHaveBeenCalledWith ||
expect(sectionClicked).toBe(false);
});
test('should handle rapid button clicks gracefully', () => {
const editBtn = document.querySelector('.edit-btn');
// Simulate rapid clicks
for (let i = 0; i < 5; i++) {
editBtn.click();
}
// Should not cause errors
expect(editBtn).toBeTruthy();
});
test('should debounce button actions', () => {
const saveBtn = document.querySelector('.save-all-btn');
let clickCount = 0;
const debouncedHandler = jest.fn(() => {
clickCount++;
});
saveBtn.addEventListener('click', debouncedHandler);
// Simulate multiple quick clicks
saveBtn.click();
saveBtn.click();
saveBtn.click();
expect(debouncedHandler).toHaveBeenCalledTimes(3);
});
});
describe('Button state management', () => {
test('should disable buttons during processing', () => {
const acceptBtn = document.querySelector('.accept-btn');
// Simulate processing state
acceptBtn.disabled = true;
expect(acceptBtn.disabled).toBe(true);
});
test('should show loading state for async operations', () => {
const saveBtn = document.querySelector('.save-all-btn');
// Simulate loading state
const originalText = saveBtn.textContent;
saveBtn.textContent = 'Saving...';
saveBtn.disabled = true;
expect(saveBtn.textContent).toBe('Saving...');
expect(saveBtn.disabled).toBe(true);
// Restore state
saveBtn.textContent = originalText;
saveBtn.disabled = false;
expect(saveBtn.textContent).toBe('Save All');
expect(saveBtn.disabled).toBe(false);
});
test('should maintain button visibility states', () => {
const buttons = {
edit: document.querySelector('.edit-btn'),
accept: document.querySelector('.accept-btn'),
cancel: document.querySelector('.cancel-btn')
};
// Default state: edit visible, accept/cancel hidden
expect(buttons.edit.style.display).not.toBe('none');
expect(buttons.accept.style.display).toBe('none');
expect(buttons.cancel.style.display).toBe('none');
});
});
describe('DOM event handling', () => {
test('should handle click events correctly', () => {
const addSectionBtn = document.querySelector('.add-section-btn');
let clicked = false;
addSectionBtn.addEventListener('click', () => {
clicked = true;
});
addSectionBtn.click();
expect(clicked).toBe(true);
});
test('should handle keyboard events for accessibility', () => {
const editBtn = document.querySelector('.edit-btn');
let keyPressed = false;
editBtn.addEventListener('keydown', (event) => {
if (event.key === 'Enter' || event.key === ' ') {
keyPressed = true;
}
});
// Simulate Enter key press
const enterEvent = new KeyboardEvent('keydown', {
key: 'Enter',
bubbles: true
});
editBtn.dispatchEvent(enterEvent);
expect(keyPressed).toBe(true);
});
test('should handle focus and blur events', () => {
const editBtn = document.querySelector('.edit-btn');
let focused = false;
let blurred = false;
editBtn.addEventListener('focus', () => {
focused = true;
});
editBtn.addEventListener('blur', () => {
blurred = true;
});
editBtn.focus();
expect(focused).toBe(true);
editBtn.blur();
expect(blurred).toBe(true);
});
});
describe('Button positioning and layout', () => {
test('should position floating controls correctly', () => {
const floatingControls = document.querySelector('.floating-controls');
// Test positioning properties
floatingControls.style.position = 'fixed';
floatingControls.style.top = '20px';
floatingControls.style.right = '20px';
expect(floatingControls.style.position).toBe('fixed');
expect(floatingControls.style.top).toBe('20px');
expect(floatingControls.style.right).toBe('20px');
});
test('should handle responsive button layouts', () => {
const sectionControls = document.querySelector('.section-controls');
// Test responsive classes
sectionControls.classList.add('responsive-controls');
expect(sectionControls.classList.contains('responsive-controls')).toBe(true);
});
test('should maintain button alignment in sections', () => {
const controls = document.querySelector('.section-controls');
const buttons = controls.querySelectorAll('button');
expect(buttons.length).toBeGreaterThan(0);
// All buttons should be in the same container
buttons.forEach(button => {
expect(button.parentElement).toBe(controls);
});
});
});
describe('Button confirmation dialogs', () => {
test('should show confirmation for destructive actions', () => {
const deleteBtn = document.querySelector('.delete-btn');
// Mock confirm dialog
window.confirm = jest.fn(() => false);
deleteBtn.addEventListener('click', () => {
if (window.confirm('Are you sure you want to delete this section?')) {
// Perform deletion
}
});
deleteBtn.click();
// Should show confirmation
expect(window.confirm).toHaveBeenCalledWith('Are you sure you want to delete this section?');
});
test('should cancel action when confirmation is denied', () => {
const deleteBtn = document.querySelector('.delete-btn');
let deleted = false;
window.confirm = jest.fn(() => false);
deleteBtn.addEventListener('click', () => {
if (window.confirm('Are you sure?')) {
deleted = true;
}
});
deleteBtn.click();
expect(deleted).toBe(false);
});
});
describe('DocumentControls integration', () => {
test('should integrate with DocumentControls class', () => {
if (documentControls) {
expect(typeof documentControls.create).toBe('function');
expect(typeof documentControls.addButton).toBe('function');
expect(typeof documentControls.setEventHandlers).toBe('function');
}
});
test('should handle button events through DocumentControls', () => {
if (!documentControls) return;
// Test that DocumentControls can manage event handlers
expect(documentControls.eventHandlers).toBeDefined();
expect(documentControls.eventHandlers instanceof Map).toBe(true);
});
test('should handle button actions through event delegation', () => {
const content = document.getElementById('content');
let actionTriggered = '';
content.addEventListener('click', (event) => {
if (event.target.matches('button[data-action]')) {
actionTriggered = event.target.getAttribute('data-action');
}
});
const editBtn = document.querySelector('.edit-btn');
editBtn.click();
expect(actionTriggered).toBe('edit');
});
});
});

View File

@@ -1,86 +0,0 @@
/**
* Component Integration Tests (Jest Version)
*
* Tests that extracted components work together properly.
* Verifies the complete workflow: Section Creation → Rendering → Editing → Saving
*/
describe('Component Integration Tests', () => {
let SectionManager, Section, DOMRenderer, FloatingMenu, EditState;
let sectionManager, domRenderer, container;
beforeAll(() => {
// Load extracted components
const sectionModule = require('../core/section-manager.js');
const domModule = require('../components/dom-renderer.js');
SectionManager = sectionModule.SectionManager;
Section = sectionModule.Section;
DOMRenderer = domModule.DOMRenderer;
FloatingMenu = domModule.FloatingMenu;
EditState = sectionModule.EditState;
});
beforeEach(() => {
// Setup fresh container and components for each test
container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
document.body.appendChild(container);
sectionManager = new SectionManager();
domRenderer = new DOMRenderer(sectionManager, container);
});
afterEach(() => {
// Cleanup
if (container && container.parentNode) {
container.parentNode.removeChild(container);
}
});
test('should load all extracted components', () => {
expect(SectionManager).toBeTruthy();
expect(Section).toBeTruthy();
expect(DOMRenderer).toBeTruthy();
expect(FloatingMenu).toBeTruthy();
expect(EditState).toBeTruthy();
});
test('should support complete section creation workflow', () => {
// Test basic functionality without complex DOM manipulation
expect(sectionManager).toBeInstanceOf(SectionManager);
expect(domRenderer).toBeInstanceOf(DOMRenderer);
// Test section creation from markdown
const testMarkdown = `# Test Header
This is test content.
![Test Image](test.jpg)`;
// Create sections from markdown (the right method)
expect(() => {
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
expect(sections.length).toBeGreaterThan(0);
}).not.toThrow();
});
test('should have core DOM rendering methods', () => {
expect(typeof domRenderer.renderAllSections).toBe('function');
expect(typeof domRenderer.showEditor).toBe('function');
expect(typeof domRenderer.findSectionElement).toBe('function');
});
test('should preserve editor showing functionality', () => {
const mockSection = {
id: 'test-section-001',
type: 'header',
content: 'Test content'
};
// Test basic editor functionality
expect(() => {
domRenderer.showEditor(mockSection.id);
}).not.toThrow();
});
});

View File

@@ -1,280 +0,0 @@
/**
* Image Editing Functionality Tests
*
* Tests image editing, positioning, and reset functionality
* Based on functionality from history/javascript-dev-tests/test_*image*.js files
*/
describe('Image Editing', () => {
let mockImageSection;
let mockImageElement;
beforeEach(() => {
// Setup DOM with image section
document.body.innerHTML = `
<div id="content">
<div class="section image-section" data-section-id="image-section-1">
<div class="section-content">
<img src="test-image.jpg" alt="Test image" class="section-image">
<div class="image-controls">
<button class="edit-image-btn">Edit Image</button>
<button class="reset-image-btn">Reset</button>
</div>
<div class="image-editor-dialog" style="display: none;">
<textarea class="alt-text-input" placeholder="Alt text"></textarea>
<input type="text" class="image-caption" placeholder="Caption">
<button class="apply-image-changes">Apply</button>
<button class="cancel-image-changes">Cancel</button>
</div>
</div>
</div>
</div>
`;
mockImageSection = document.querySelector('.image-section');
mockImageElement = document.querySelector('.section-image');
});
afterEach(() => {
document.body.innerHTML = '';
jest.clearAllMocks();
});
describe('Image editor dialog', () => {
test('should show image editor when edit button is clicked', () => {
const editButton = document.querySelector('.edit-image-btn');
const dialog = document.querySelector('.image-editor-dialog');
expect(editButton).toBeTruthy();
expect(dialog).toBeTruthy();
// Simulate edit button click
editButton.click();
// In real implementation, dialog should become visible
expect(dialog.style.display).toBe('none'); // Initially hidden
});
test('should populate current alt text and caption', () => {
const altTextInput = document.querySelector('.alt-text-input');
const captionInput = document.querySelector('.image-caption');
expect(altTextInput).toBeTruthy();
expect(captionInput).toBeTruthy();
// Simulate populating current values
const currentAlt = mockImageElement.alt;
altTextInput.value = currentAlt;
expect(altTextInput.value).toBe(currentAlt);
});
test('should handle dialog positioning correctly', () => {
const dialog = document.querySelector('.image-editor-dialog');
// Test that dialog positioning can be set
dialog.style.position = 'absolute';
dialog.style.top = '100px';
dialog.style.left = '100px';
expect(dialog.style.position).toBe('absolute');
expect(dialog.style.top).toBe('100px');
expect(dialog.style.left).toBe('100px');
});
});
describe('Image modifications', () => {
test('should update alt text when applied', () => {
const altTextInput = document.querySelector('.alt-text-input');
const applyButton = document.querySelector('.apply-image-changes');
const newAltText = 'Updated alt text for image';
altTextInput.value = newAltText;
// Simulate apply action
applyButton.click();
// In real implementation, image alt text should be updated
expect(altTextInput.value).toBe(newAltText);
});
test('should update image caption when applied', () => {
const captionInput = document.querySelector('.image-caption');
const newCaption = 'Updated image caption';
captionInput.value = newCaption;
expect(captionInput.value).toBe(newCaption);
});
test('should validate required fields', () => {
const altTextInput = document.querySelector('.alt-text-input');
// Test empty alt text validation
altTextInput.value = '';
const isEmpty = altTextInput.value.trim() === '';
expect(isEmpty).toBe(true);
// Test filled alt text
altTextInput.value = 'Valid alt text';
const isFilled = altTextInput.value.trim() !== '';
expect(isFilled).toBe(true);
});
});
describe('Image reset functionality', () => {
test('should reset image to original state', () => {
const resetButton = document.querySelector('.reset-image-btn');
const altTextInput = document.querySelector('.alt-text-input');
// Store original values
const originalAlt = mockImageElement.alt;
// Modify values
altTextInput.value = 'Modified alt text';
mockImageElement.alt = 'Modified alt';
// Simulate reset
resetButton.click();
// In real implementation, should restore original values
expect(resetButton).toBeTruthy();
});
test('should confirm before resetting changes', () => {
const resetButton = document.querySelector('.reset-image-btn');
// Mock confirm dialog
window.confirm = jest.fn(() => true);
resetButton.click();
// In real implementation, should show confirmation
expect(resetButton).toBeTruthy();
});
test('should preserve original image data', () => {
// Test that original image data is stored
const originalData = {
src: mockImageElement.src,
alt: mockImageElement.alt,
caption: ''
};
expect(originalData.src).toBeTruthy();
expect(typeof originalData.alt).toBe('string');
expect(typeof originalData.caption).toBe('string');
});
});
describe('Image editor UI controls', () => {
test('should handle cancel button correctly', () => {
const cancelButton = document.querySelector('.cancel-image-changes');
const dialog = document.querySelector('.image-editor-dialog');
cancelButton.click();
// In real implementation, should close dialog without saving
expect(cancelButton).toBeTruthy();
expect(dialog).toBeTruthy();
});
test('should close dialog after applying changes', () => {
const applyButton = document.querySelector('.apply-image-changes');
const dialog = document.querySelector('.image-editor-dialog');
applyButton.click();
// In real implementation, should close dialog after applying
expect(applyButton).toBeTruthy();
expect(dialog.style.display).toBe('none');
});
test('should handle escape key to cancel', () => {
const dialog = document.querySelector('.image-editor-dialog');
const altTextInput = document.querySelector('.alt-text-input');
// Simulate escape key press
const escapeEvent = new KeyboardEvent('keydown', {
key: 'Escape',
bubbles: true
});
altTextInput.dispatchEvent(escapeEvent);
// In real implementation, should close dialog
expect(dialog).toBeTruthy();
});
});
describe('Advanced image editor features', () => {
test('should support image URL editing', () => {
const imageUrl = mockImageElement.src;
// Test URL validation
const isValidUrl = /^https?:\/\//.test(imageUrl) || imageUrl.startsWith('/') || imageUrl.startsWith('./');
// Local files and URLs should be valid
expect(typeof imageUrl).toBe('string');
});
test('should handle image loading errors', () => {
const errorHandler = jest.fn();
mockImageElement.onerror = errorHandler;
mockImageElement.src = 'invalid-image-url.jpg';
// In real implementation, should handle image load errors
expect(mockImageElement.onerror).toBe(errorHandler);
});
test('should support image alignment options', () => {
const alignmentOptions = ['left', 'center', 'right', 'full-width'];
alignmentOptions.forEach(alignment => {
mockImageElement.className = `section-image align-${alignment}`;
expect(mockImageElement.className).toContain(`align-${alignment}`);
});
});
test('should handle responsive image sizing', () => {
// Test responsive image attributes
mockImageElement.style.maxWidth = '100%';
mockImageElement.style.height = 'auto';
expect(mockImageElement.style.maxWidth).toBe('100%');
expect(mockImageElement.style.height).toBe('auto');
});
});
describe('Image section integration', () => {
test('should maintain section integrity during image editing', () => {
const sectionId = mockImageSection.getAttribute('data-section-id');
expect(sectionId).toBeTruthy();
expect(mockImageSection.classList.contains('image-section')).toBe(true);
});
test('should handle multiple images in one section', () => {
// Add another image to the section
const secondImage = document.createElement('img');
secondImage.src = 'second-image.jpg';
secondImage.alt = 'Second image';
secondImage.className = 'section-image';
mockImageSection.querySelector('.section-content').appendChild(secondImage);
const images = mockImageSection.querySelectorAll('.section-image');
expect(images.length).toBe(2);
});
test('should preserve section order when editing images', () => {
const sectionContent = mockImageSection.querySelector('.section-content');
const children = Array.from(sectionContent.children);
const imageIndex = children.findIndex(child => child.tagName === 'IMG');
expect(imageIndex).toBeGreaterThanOrEqual(0);
});
});
});

View File

@@ -1,26 +0,0 @@
/**
* Jest Setup File for JavaScript UI Tests
*
* Sets up environment and global utilities for testing.
* Jest with jsdom environment already provides DOM globals.
*/
// Add TextEncoder/TextDecoder polyfills for Node.js compatibility
const { TextEncoder, TextDecoder } = require('util');
global.TextEncoder = TextEncoder;
global.TextDecoder = TextDecoder;
// Mock console methods to reduce noise in tests
const originalLog = console.log;
console.log = (...args) => {
// Only log if DEBUG_TESTS environment variable is set
if (process.env.DEBUG_TESTS) {
originalLog(...args);
}
};
// Setup DOM fixtures after page load
beforeEach(() => {
// Reset document body for each test
document.body.innerHTML = '<div id="content"></div>';
});

View File

@@ -1,219 +0,0 @@
/**
* Keyboard Shortcuts Functionality Tests
*
* Tests keyboard shortcuts for section editing (Ctrl+Enter, Escape, etc.)
* Based on functionality from history/javascript-dev-tests/test_keyboard_shortcuts.js
*/
describe('Keyboard Shortcuts', () => {
let domRenderer;
let mockTextarea;
beforeEach(() => {
// Setup DOM
document.body.innerHTML = `
<div id="content">
<div class="section" data-section-id="test-section">
<textarea class="edit-textarea">Test content</textarea>
</div>
</div>
`;
// Load components
require('../components/dom-renderer.js');
require('../core/section-manager.js');
// Mock SectionManager with event system
const mockSectionManager = {
on: jest.fn(),
emit: jest.fn(),
handleSectionSplit: jest.fn(),
sections: []
};
if (global.DOMRenderer) {
// Create DOMRenderer with mocked dependencies
try {
domRenderer = new global.DOMRenderer(mockSectionManager, document.getElementById('content'));
} catch (error) {
// If constructor fails, create a mock with the methods we need
domRenderer = {
applyChanges: jest.fn(),
cancelEdit: jest.fn()
};
}
}
mockTextarea = document.querySelector('.edit-textarea');
});
afterEach(() => {
document.body.innerHTML = '';
jest.clearAllMocks();
});
describe('Ctrl+Enter shortcut (Accept Changes)', () => {
test('should apply changes when Ctrl+Enter is pressed', () => {
if (!mockTextarea) {
console.warn('Textarea not available, skipping test');
return;
}
// Test that Ctrl+Enter event can be dispatched
const ctrlEnterEvent = new KeyboardEvent('keydown', {
key: 'Enter',
ctrlKey: true,
bubbles: true
});
let eventFired = false;
mockTextarea.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.key === 'Enter') {
eventFired = true;
}
});
mockTextarea.dispatchEvent(ctrlEnterEvent);
// Verify event was handled
expect(eventFired).toBe(true);
});
test('should prevent default behavior on Ctrl+Enter', () => {
if (!mockTextarea) return;
const preventDefault = jest.fn();
const ctrlEnterEvent = new KeyboardEvent('keydown', {
key: 'Enter',
ctrlKey: true,
bubbles: true
});
// Mock preventDefault
ctrlEnterEvent.preventDefault = preventDefault;
mockTextarea.dispatchEvent(ctrlEnterEvent);
// Note: In real implementation, preventDefault should be called
// This test documents the expected behavior
expect(true).toBe(true); // Placeholder for actual implementation check
});
});
describe('Escape shortcut (Cancel Changes)', () => {
test('should cancel changes when Escape is pressed', () => {
if (!mockTextarea) {
console.warn('Textarea not available, skipping test');
return;
}
// Test that Escape event can be dispatched
const escapeEvent = new KeyboardEvent('keydown', {
key: 'Escape',
bubbles: true
});
let escapePressed = false;
mockTextarea.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
escapePressed = true;
}
});
mockTextarea.dispatchEvent(escapeEvent);
// Verify escape was detected
expect(escapePressed).toBe(true);
});
test('should restore original content on Escape', () => {
if (!mockTextarea) return;
const originalContent = 'Original content';
mockTextarea.setAttribute('data-original-content', originalContent);
mockTextarea.value = 'Modified content';
const escapeEvent = new KeyboardEvent('keydown', {
key: 'Escape',
bubbles: true
});
mockTextarea.dispatchEvent(escapeEvent);
// In real implementation, content should be restored
// This test documents the expected behavior
expect(mockTextarea.getAttribute('data-original-content')).toBe(originalContent);
});
});
describe('Keyboard shortcuts integration', () => {
test('should bind keyboard handlers to textareas', () => {
const textarea = document.createElement('textarea');
textarea.className = 'edit-textarea';
document.body.appendChild(textarea);
// Check if event listeners can be added (integration test)
let listenerAdded = false;
const originalAddEventListener = textarea.addEventListener;
textarea.addEventListener = jest.fn((event, handler) => {
if (event === 'keydown') {
listenerAdded = true;
}
return originalAddEventListener.call(textarea, event, handler);
});
// In real implementation, DOMRenderer should bind keydown listeners
// This test ensures the capability exists
expect(textarea.addEventListener).toBeDefined();
expect(typeof textarea.addEventListener).toBe('function');
});
test('should handle multiple keyboard events correctly', () => {
if (!mockTextarea) return;
const events = [
{ key: 'Enter', ctrlKey: true },
{ key: 'Escape', ctrlKey: false },
{ key: 'Tab', ctrlKey: false }
];
events.forEach(eventData => {
const event = new KeyboardEvent('keydown', {
...eventData,
bubbles: true
});
// Should not throw errors when handling various key events
expect(() => {
mockTextarea.dispatchEvent(event);
}).not.toThrow();
});
});
});
describe('Keyboard shortcuts accessibility', () => {
test('should provide keyboard alternatives to mouse actions', () => {
// This test ensures keyboard accessibility is maintained
const shortcuts = [
{ key: 'Enter', ctrlKey: true, action: 'apply' },
{ key: 'Escape', ctrlKey: false, action: 'cancel' }
];
shortcuts.forEach(shortcut => {
expect(shortcut.key).toBeDefined();
expect(shortcut.action).toBeDefined();
});
});
test('should work with screen readers and assistive technology', () => {
if (!mockTextarea) return;
// Test ARIA attributes and accessibility features
mockTextarea.setAttribute('aria-label', 'Edit section content');
mockTextarea.setAttribute('role', 'textbox');
expect(mockTextarea.getAttribute('aria-label')).toBeTruthy();
expect(mockTextarea.getAttribute('role')).toBe('textbox');
});
});
});

View File

@@ -1,216 +0,0 @@
#!/usr/bin/env node
/**
* TDD Test Runner for JavaScript Refactoring
*
* Drives component extraction and testing during architecture refactoring.
* Ensures all functionality remains stable while achieving separation of concerns.
*/
class RefactorTestRunner {
constructor() {
this.tests = [];
this.passed = 0;
this.failed = 0;
this.currentSuite = null;
this.setupDOM();
}
setupDOM() {
// Set up minimal DOM environment for testing
if (typeof document === 'undefined') {
const { JSDOM } = require('jsdom');
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {
url: 'http://localhost',
pretendToBeVisual: true,
resources: 'usable'
});
global.window = dom.window;
global.document = dom.window.document;
global.HTMLElement = dom.window.HTMLElement;
global.Event = dom.window.Event;
global.CustomEvent = dom.window.CustomEvent;
// Only set navigator if it doesn't exist
if (typeof global.navigator === 'undefined') {
global.navigator = dom.window.navigator;
}
}
}
describe(suiteName, fn) {
console.log(`\n📁 ${suiteName}`);
this.currentSuite = suiteName;
fn();
this.currentSuite = null;
}
it(testName, fn) {
const fullName = this.currentSuite ? `${this.currentSuite}: ${testName}` : testName;
try {
fn();
console.log(`${testName}`);
this.passed++;
} catch (error) {
console.log(`${testName}`);
console.log(` Error: ${error.message}`);
if (error.stack) {
console.log(` Stack: ${error.stack.split('\n')[1]?.trim()}`);
}
this.failed++;
}
}
expect(actual) {
return {
toBe: (expected) => {
if (actual !== expected) {
throw new Error(`Expected ${expected}, got ${actual}`);
}
},
toBeTruthy: () => {
if (!actual) {
throw new Error(`Expected truthy value, got ${actual}`);
}
},
toBeFalsy: () => {
if (actual) {
throw new Error(`Expected falsy value, got ${actual}`);
}
},
toEqual: (expected) => {
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
throw new Error(`Expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
}
},
toContain: (expected) => {
if (!actual.includes(expected)) {
throw new Error(`Expected ${actual} to contain ${expected}`);
}
},
toHaveProperty: (property) => {
if (!(property in actual)) {
throw new Error(`Expected object to have property ${property}`);
}
},
toBeInstanceOf: (expectedClass) => {
if (!(actual instanceof expectedClass)) {
throw new Error(`Expected instance of ${expectedClass.name}, got ${actual.constructor.name}`);
}
}
};
}
/**
* Test that a component can be extracted from the monolith without breaking functionality
*/
testComponentExtraction(componentName, extractFn, originalTests) {
this.describe(`Component Extraction: ${componentName}`, () => {
this.it('should extract without syntax errors', () => {
try {
const component = extractFn();
this.expect(component).toBeTruthy();
} catch (error) {
throw new Error(`Component extraction failed: ${error.message}`);
}
});
this.it('should maintain original API', () => {
const component = extractFn();
originalTests.forEach(test => {
try {
test(component);
} catch (error) {
throw new Error(`API compatibility test failed: ${error.message}`);
}
});
});
});
}
/**
* Test component integration after extraction
*/
testComponentIntegration(components, integrationTests) {
this.describe('Component Integration', () => {
integrationTests.forEach((test, index) => {
this.it(`integration test ${index + 1}`, () => {
test(components);
});
});
});
}
/**
* Setup test environment with mock dependencies
*/
setupTestEnvironment() {
// Create test container
const container = document.createElement('div');
container.id = 'test-container';
container.innerHTML = '<div id="markdown-content"></div>';
document.body.appendChild(container);
// Mock any global dependencies
global.mockSectionManager = {
sections: new Map(),
createSectionsFromMarkdown: () => [],
startEditing: () => true,
stopEditing: () => true,
getAllSections: () => []
};
return { container };
}
/**
* Cleanup test environment
*/
cleanupTestEnvironment() {
const container = document.getElementById('test-container');
if (container) {
container.remove();
}
// Clear any global mocks
delete global.mockSectionManager;
}
async run() {
console.log('🧪 TDD Refactoring Test Runner Starting...\n');
const startTime = Date.now();
// Run all collected tests
// Tests will be added by importing component test files
const endTime = Date.now();
const duration = endTime - startTime;
console.log(`\n📊 Test Results:`);
console.log(` ✅ Passed: ${this.passed}`);
console.log(` ❌ Failed: ${this.failed}`);
console.log(` ⏱️ Duration: ${duration}ms`);
if (this.failed > 0) {
console.log(`\n${this.failed} test(s) failed. Refactoring should not proceed.`);
process.exit(1);
} else {
console.log(`\n✅ All tests passed! Refactoring is safe to continue.`);
}
}
}
// Export for use in component tests
if (typeof module !== 'undefined' && module.exports) {
module.exports = { RefactorTestRunner };
}
// Export for browser use
if (typeof window !== 'undefined') {
window.RefactorTestRunner = RefactorTestRunner;
}
module.exports = RefactorTestRunner;

View File

@@ -1,267 +0,0 @@
/**
* Section Splitting Functionality Tests
*
* Tests dynamic section splitting when headings are detected
* Based on functionality from history/javascript-dev-tests/test_section_splitting.js
*/
describe('Section Splitting', () => {
let sectionManager;
beforeEach(() => {
// Setup DOM
document.body.innerHTML = `
<div id="content">
<div class="section" data-section-id="main-section">
<div class="section-content">
<p>Original content</p>
</div>
</div>
</div>
`;
// Load components
require('../core/section-manager.js');
if (global.SectionManager) {
sectionManager = new global.SectionManager();
}
});
afterEach(() => {
document.body.innerHTML = '';
jest.clearAllMocks();
});
describe('Heading detection', () => {
test('should detect new headings in content', () => {
const textWithHeading = `
This is some content.
# New Heading
This should be a new section.
`;
// Test heading detection with regex
const lines = textWithHeading.trim().split('\n');
const headingLine = lines.find(line => /^#+ /.test(line.trim()));
expect(headingLine).toBeTruthy();
expect(headingLine.trim()).toBe('# New Heading');
});
test('should identify different heading levels', () => {
const headingTests = [
{ text: '# Heading 1', level: 1 },
{ text: '## Heading 2', level: 2 },
{ text: '### Heading 3', level: 3 },
{ text: '#### Heading 4', level: 4 }
];
headingTests.forEach(({ text, level }) => {
const match = text.match(/^(#+) /);
expect(match).toBeTruthy();
if (match) {
expect(match[1].length).toBe(level);
}
});
});
test('should distinguish headings from regular text', () => {
const testCases = [
{ text: '# This is a heading', isHeading: true },
{ text: 'This is not a heading', isHeading: false },
{ text: 'Neither is this # hash in middle', isHeading: false },
{ text: '## Another heading', isHeading: true }
];
testCases.forEach(({ text, isHeading }) => {
const match = /^#+\s/.test(text.trim());
expect(match).toBe(isHeading);
});
});
});
describe('Section splitting logic', () => {
test('should split content when heading is detected', () => {
const originalContent = 'Original content without headings';
const newContent = `
${originalContent}
# New Section
New section content
`;
// Simulate section splitting logic
const parts = newContent.split(/\n(?=#)/);
if (parts.length > 1) {
expect(parts.length).toBeGreaterThan(1);
expect(parts[0]).toContain('Original content');
expect(parts[1]).toContain('# New Section');
}
});
test('should preserve content when no headings are present', () => {
const content = 'Just regular content without any headings';
const parts = content.split(/\n(?=#)/);
expect(parts.length).toBe(1);
expect(parts[0]).toBe(content);
});
test('should handle multiple headings correctly', () => {
const contentWithMultipleHeadings = `Initial content
# First Heading
First section content
## Second Heading
Second section content
# Third Heading
Third section content`;
// Split on lines that start with headings
const parts = contentWithMultipleHeadings.split(/\n(?=#)/);
// Should split into multiple sections
expect(parts.length).toBeGreaterThanOrEqual(2);
// Find heading lines
const headings = contentWithMultipleHeadings.match(/^#+.*$/gm);
expect(headings).toBeTruthy();
expect(headings.length).toBe(3);
});
});
describe('SectionManager integration', () => {
test('should have handleSectionSplit method', () => {
if (!sectionManager) {
console.warn('SectionManager not available, skipping test');
return;
}
expect(typeof sectionManager.handleSectionSplit).toBe('function');
});
test('should maintain section state during splits', () => {
if (!sectionManager) return;
const originalSectionCount = document.querySelectorAll('.section').length;
// Mock section splitting
const mockNewSection = document.createElement('div');
mockNewSection.className = 'section';
mockNewSection.setAttribute('data-section-id', 'split-section');
if (originalSectionCount > 0) {
expect(originalSectionCount).toBeGreaterThan(0);
}
});
});
describe('Dynamic section creation', () => {
test('should create new section elements when splitting', () => {
const sectionContent = `
Original content
# New Section Title
New section content
`;
// Simulate section creation
const newSection = document.createElement('div');
newSection.className = 'section';
newSection.setAttribute('data-section-id', 'generated-section-id');
const contentDiv = document.createElement('div');
contentDiv.className = 'section-content';
contentDiv.textContent = 'New section content';
newSection.appendChild(contentDiv);
expect(newSection.className).toBe('section');
expect(newSection.getAttribute('data-section-id')).toBeTruthy();
expect(newSection.querySelector('.section-content')).toBeTruthy();
});
test('should generate unique section IDs', () => {
const headingText = 'My New Section';
// Simulate ID generation from heading
const sectionId = headingText
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '');
expect(sectionId).toBe('my-new-section');
});
test('should preserve section hierarchy', () => {
const hierarchicalContent = `
# Main Section
Main content
## Subsection
Sub content
### Sub-subsection
Sub-sub content
`;
const headings = hierarchicalContent.match(/^#+.*$/gm);
if (headings) {
expect(headings.length).toBe(3);
expect(headings[0]).toMatch(/^# /);
expect(headings[1]).toMatch(/^## /);
expect(headings[2]).toMatch(/^### /);
}
});
});
describe('Section splitting edge cases', () => {
test('should handle empty headings gracefully', () => {
const contentWithEmptyHeading = `
Content before
#
Content after
`;
const parts = contentWithEmptyHeading.split(/\n(?=#)/);
expect(parts.length).toBeGreaterThanOrEqual(1);
});
test('should handle headings at the start of content', () => {
const contentStartingWithHeading = `# First Heading
Content for first section
# Second Heading
Content for second section
`;
const parts = contentStartingWithHeading.split(/\n(?=#)/);
expect(parts[0]).toContain('# First Heading');
});
test('should handle malformed headings', () => {
const malformedHeadings = [
'#NoSpace',
'# ',
'########## Too many hashes',
'Not a heading # at all'
];
malformedHeadings.forEach(text => {
const isValidHeading = /^#{1,6}\s+\S/.test(text);
// Most should be invalid except properly formatted ones
expect(typeof isValidHeading).toBe('boolean');
});
});
});
});

View File

@@ -1,139 +0,0 @@
/**
* Jest Test Setup for TestDrive-JSUI
*
* Sets up the testing environment for JavaScript UI components.
* Provides DOM mocking, global utilities, and test helpers.
*/
// Mock DOM globals that might be missing in JSDOM
global.ResizeObserver = jest.fn().mockImplementation(() => ({
observe: jest.fn(),
unobserve: jest.fn(),
disconnect: jest.fn(),
}));
global.IntersectionObserver = jest.fn().mockImplementation(() => ({
observe: jest.fn(),
unobserve: jest.fn(),
disconnect: jest.fn(),
}));
// Mock window.matchMedia
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
// Mock local storage
const localStorageMock = {
getItem: jest.fn(),
setItem: jest.fn(),
removeItem: jest.fn(),
clear: jest.fn(),
};
global.localStorage = localStorageMock;
// Mock session storage
const sessionStorageMock = {
getItem: jest.fn(),
setItem: jest.fn(),
removeItem: jest.fn(),
clear: jest.fn(),
};
global.sessionStorage = sessionStorageMock;
// Global test utilities
global.testUtils = {
/**
* Create a mock DOM element with specified tag and attributes
*/
createElement: (tag, attributes = {}) => {
const element = document.createElement(tag);
Object.entries(attributes).forEach(([key, value]) => {
element.setAttribute(key, value);
});
return element;
},
/**
* Create a test markdown content div
*/
createMarkdownContent: (content = '# Test Content') => {
const div = document.createElement('div');
div.id = 'markdown-content';
div.innerHTML = content;
return div;
},
/**
* Wait for next tick (useful for async operations)
*/
nextTick: () => new Promise(resolve => setTimeout(resolve, 0)),
/**
* Simulate user interaction events
*/
simulateEvent: (element, eventType, eventProperties = {}) => {
const event = new Event(eventType, { bubbles: true, ...eventProperties });
Object.entries(eventProperties).forEach(([key, value]) => {
event[key] = value;
});
element.dispatchEvent(event);
return event;
},
/**
* Clean up DOM after each test
*/
cleanupDOM: () => {
document.body.innerHTML = '';
document.head.innerHTML = '';
}
};
// Setup and teardown
beforeEach(() => {
// Reset mocks
jest.clearAllMocks();
// Reset localStorage/sessionStorage
localStorageMock.getItem.mockClear();
localStorageMock.setItem.mockClear();
localStorageMock.removeItem.mockClear();
localStorageMock.clear.mockClear();
sessionStorageMock.getItem.mockClear();
sessionStorageMock.setItem.mockClear();
sessionStorageMock.removeItem.mockClear();
sessionStorageMock.clear.mockClear();
});
afterEach(() => {
// Clean up DOM
global.testUtils.cleanupDOM();
// Clean up any timers
jest.runOnlyPendingTimers();
jest.useRealTimers();
});
// Console helpers for test debugging
global.console = {
...console,
// Keep these methods for test debugging
log: console.log,
warn: console.warn,
error: console.error,
// Mock these to avoid noise in tests
info: jest.fn(),
debug: jest.fn(),
};

View File

@@ -1,521 +0,0 @@
#!/usr/bin/env node
/**
* Comprehensive Component Integration Test
*
* Tests that extracted components work together properly.
* Verifies the complete workflow: Section Creation → Rendering → Editing → Saving
*/
const RefactorTestRunner = require('./refactor-test-runner.js');
const runner = new RefactorTestRunner();
runner.describe('Component Integration Tests', () => {
runner.it('should load all extracted components', () => {
try {
// Load extracted components
const sectionModule = require('../core/section-manager.js');
const domModule = require('../components/dom-renderer.js');
runner.expect(sectionModule.SectionManager).toBeTruthy();
runner.expect(sectionModule.Section).toBeTruthy();
runner.expect(domModule.DOMRenderer).toBeTruthy();
runner.expect(domModule.FloatingMenu).toBeTruthy();
// Set globals for other tests
global.ExtractedSectionManager = sectionModule.SectionManager;
global.ExtractedSection = sectionModule.Section;
global.ExtractedDOMRenderer = domModule.DOMRenderer;
global.ExtractedFloatingMenu = domModule.FloatingMenu;
global.ExtractedEditState = sectionModule.EditState;
} catch (error) {
throw new Error(`Failed to load extracted components: ${error.message}`);
}
});
runner.it('should support complete section creation workflow', () => {
const SectionManager = global.ExtractedSectionManager;
const DOMRenderer = global.ExtractedDOMRenderer;
// Setup
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
document.body.appendChild(container);
const sectionManager = new SectionManager();
const domRenderer = new DOMRenderer(sectionManager, container);
// Test workflow: Create sections from markdown
const testMarkdown = `# Main Heading
This is the introduction content.
## Subheading One
Content for first subsection.
![Test Image](https://example.com/image.jpg)
## Subheading Two
Content for second subsection.`;
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
// Verify sections were created
// Expected: heading+paragraph, heading+paragraph, image, heading+paragraph = 4 sections
runner.expect(sections.length).toBe(4);
runner.expect(sections[0].type).toBe('heading');
runner.expect(sections[2].type).toBe('image');
// Verify DOM rendering
domRenderer.renderAllSections(sections);
const renderedElements = container.querySelectorAll('.ui-edit-section');
runner.expect(renderedElements.length).toBe(sections.length);
// Cleanup
document.body.removeChild(container);
});
runner.it('should support complete editing workflow', () => {
const SectionManager = global.ExtractedSectionManager;
const DOMRenderer = global.ExtractedDOMRenderer;
const EditState = global.ExtractedEditState;
// Setup
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
document.body.appendChild(container);
const sectionManager = new SectionManager();
const domRenderer = new DOMRenderer(sectionManager, container);
// Create and render sections
const testMarkdown = '# Test Heading\nOriginal content here.';
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
domRenderer.renderAllSections(sections);
const sectionId = sections[0].id;
const section = sectionManager.sections.get(sectionId);
// Test workflow: Start editing
runner.expect(section.state).toBe(EditState.ORIGINAL);
runner.expect(section.isEditing()).toBeFalsy();
const content = sectionManager.startEditing(sectionId);
runner.expect(content).toContain('Test Heading');
runner.expect(section.isEditing()).toBeTruthy();
runner.expect(section.state).toBe(EditState.EDITING);
// Test workflow: Update content
const newContent = '# Updated Heading\nModified content here.';
sectionManager.updateContent(sectionId, newContent);
runner.expect(section.editingMarkdown).toBe(newContent);
// Test workflow: Accept changes
sectionManager.acceptChanges(sectionId);
runner.expect(section.currentMarkdown).toBe(newContent);
runner.expect(section.state).toBe(EditState.SAVED);
runner.expect(section.isEditing()).toBeFalsy();
// Cleanup
document.body.removeChild(container);
});
runner.it('should support accept/cancel button functionality', () => {
const SectionManager = global.ExtractedSectionManager;
const DOMRenderer = global.ExtractedDOMRenderer;
// Setup
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
document.body.appendChild(container);
const sectionManager = new SectionManager();
const domRenderer = new DOMRenderer(sectionManager, container);
// Create and render sections
const testMarkdown = '# Test Heading\nOriginal content here.';
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
domRenderer.renderAllSections(sections);
const sectionId = sections[0].id;
const section = sectionManager.sections.get(sectionId);
// Start editing to trigger floating menu with buttons
sectionManager.startEditing(sectionId);
// Check if floating menu exists
runner.expect(domRenderer.currentFloatingMenu).toBeTruthy();
runner.expect(domRenderer.currentFloatingMenu.isVisible).toBeTruthy();
// Find buttons in the floating menu
const menuElement = domRenderer.currentFloatingMenu.element;
runner.expect(menuElement).toBeTruthy();
const buttons = menuElement.querySelectorAll('button');
runner.expect(buttons.length >= 2).toBeTruthy(); // At least Accept and Cancel buttons
const acceptBtn = Array.from(buttons).find(btn => btn.textContent === 'Accept');
const cancelBtn = Array.from(buttons).find(btn => btn.textContent === 'Cancel');
runner.expect(acceptBtn).toBeTruthy();
runner.expect(cancelBtn).toBeTruthy();
// Test Accept button functionality
runner.expect(section.isEditing()).toBeTruthy();
// Simulate updating content and clicking Accept
const textarea = menuElement.querySelector('textarea');
runner.expect(textarea).toBeTruthy();
textarea.value = '# Updated Heading\nUpdated content via button.';
acceptBtn.click();
// After clicking Accept, section should be saved and menu hidden
runner.expect(section.isEditing()).toBeFalsy();
runner.expect(section.currentMarkdown).toContain('Updated Heading');
runner.expect(domRenderer.currentFloatingMenu).toBeFalsy();
// Cleanup
document.body.removeChild(container);
});
runner.it('should support cancel button functionality', () => {
const SectionManager = global.ExtractedSectionManager;
const DOMRenderer = global.ExtractedDOMRenderer;
// Setup
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
document.body.appendChild(container);
const sectionManager = new SectionManager();
const domRenderer = new DOMRenderer(sectionManager, container);
// Create and render sections
const testMarkdown = '# Original Heading\nOriginal content here.';
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
domRenderer.renderAllSections(sections);
const sectionId = sections[0].id;
const section = sectionManager.sections.get(sectionId);
// Start editing
sectionManager.startEditing(sectionId);
// Find buttons in the floating menu
const menuElement = domRenderer.currentFloatingMenu.element;
const cancelBtn = Array.from(menuElement.querySelectorAll('button')).find(btn => btn.textContent === 'Cancel');
runner.expect(cancelBtn).toBeTruthy();
runner.expect(section.isEditing()).toBeTruthy();
// Simulate changing content but then canceling
const textarea = menuElement.querySelector('textarea');
textarea.value = '# Changed Heading\nThis should be discarded.';
cancelBtn.click();
// After clicking Cancel, section should not be saved and menu hidden
runner.expect(section.isEditing()).toBeFalsy();
runner.expect(section.currentMarkdown).toContain('Original Heading'); // Original content preserved
runner.expect(domRenderer.currentFloatingMenu).toBeFalsy();
// Cleanup
document.body.removeChild(container);
});
runner.it('should support event-driven communication', () => {
const SectionManager = global.ExtractedSectionManager;
const DOMRenderer = global.ExtractedDOMRenderer;
// Setup
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
document.body.appendChild(container);
const sectionManager = new SectionManager();
const domRenderer = new DOMRenderer(sectionManager, container);
// Track events
let sectionsCreatedEvent = null;
let editStartedEvent = null;
sectionManager.on('sections-created', (data) => {
sectionsCreatedEvent = data;
});
sectionManager.on('edit-started', (data) => {
editStartedEvent = data;
});
// Test event: sections-created
const testMarkdown = '# Test\nContent';
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
runner.expect(sectionsCreatedEvent).toBeTruthy();
runner.expect(sectionsCreatedEvent.sections).toEqual(sections);
runner.expect(sectionsCreatedEvent.count).toBe(1);
// Test event: edit-started
const sectionId = sections[0].id;
sectionManager.startEditing(sectionId);
runner.expect(editStartedEvent).toBeTruthy();
runner.expect(editStartedEvent.sectionId).toBe(sectionId);
runner.expect(editStartedEvent.content).toContain('Test');
// Cleanup
document.body.removeChild(container);
});
runner.it('should support section type detection and rendering', () => {
const SectionManager = global.ExtractedSectionManager;
const DOMRenderer = global.ExtractedDOMRenderer;
const Section = global.ExtractedSection;
// Setup
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
document.body.appendChild(container);
const sectionManager = new SectionManager();
const domRenderer = new DOMRenderer(sectionManager, container);
// Test different section types
const testMarkdown = `# Heading Section
Regular paragraph content.
![Image Section](https://example.com/test.jpg)
\`\`\`javascript
// Code section
console.log('test');
\`\`\``;
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
// Verify type detection - adjusted for actual parsing behavior
// Expected: heading+paragraph, image, code = 3 sections
runner.expect(sections[0].type).toBe('heading'); // Combined heading+paragraph
runner.expect(sections[1].type).toBe('image'); // Image section
runner.expect(sections[2].type).toBe('code'); // Code section
// Verify image detection
runner.expect(sections[1].isImage()).toBeTruthy(); // Image is now at index 1
runner.expect(sections[0].isImage()).toBeFalsy();
// Verify rendering handles different types
domRenderer.renderAllSections(sections);
const renderedElements = container.querySelectorAll('.ui-edit-section');
runner.expect(renderedElements.length).toBe(sections.length);
// Cleanup
document.body.removeChild(container);
});
runner.it('should support FloatingMenu integration', () => {
const SectionManager = global.ExtractedSectionManager;
const DOMRenderer = global.ExtractedDOMRenderer;
const FloatingMenu = global.ExtractedFloatingMenu;
// Setup
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
document.body.appendChild(container);
const sectionManager = new SectionManager();
const domRenderer = new DOMRenderer(sectionManager, container);
// Create and render sections
const testMarkdown = '# Test Heading\nTest content';
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
domRenderer.renderAllSections(sections);
const sectionId = sections[0].id;
// Test showing editor (which uses FloatingMenu)
domRenderer.showEditor(sectionId, 'test content');
// Verify floating menu state
runner.expect(domRenderer.currentFloatingMenu).toBeTruthy();
runner.expect(domRenderer.currentFloatingMenu.sectionId).toBe(sectionId);
runner.expect(domRenderer.currentFloatingMenu.isVisible).toBeTruthy();
runner.expect(domRenderer.editingSections.has(sectionId)).toBeTruthy();
// Test hiding editor
domRenderer.hideCurrentEditor();
runner.expect(domRenderer.currentFloatingMenu).toBeFalsy();
runner.expect(domRenderer.editingSections.has(sectionId)).toBeFalsy();
// Cleanup
document.body.removeChild(container);
});
runner.it('should support complete click-to-edit workflow', () => {
const SectionManager = global.ExtractedSectionManager;
const DOMRenderer = global.ExtractedDOMRenderer;
// Setup
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
document.body.appendChild(container);
const sectionManager = new SectionManager();
const domRenderer = new DOMRenderer(sectionManager, container);
// Create and render sections
const testMarkdown = '# Test Heading\nTest content for editing';
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
domRenderer.renderAllSections(sections);
const sectionId = sections[0].id;
const element = domRenderer.findSectionElement(sectionId);
// Simulate click event
const clickEvent = new Event('click', { bubbles: true });
Object.defineProperty(clickEvent, 'target', { value: element });
// Test complete workflow
domRenderer.handleSectionClick(clickEvent);
// Verify editing state was triggered
const section = sectionManager.sections.get(sectionId);
runner.expect(section.isEditing()).toBeTruthy();
runner.expect(domRenderer.editingSections.has(sectionId)).toBeTruthy();
runner.expect(domRenderer.currentFloatingMenu).toBeTruthy();
// Cleanup
document.body.removeChild(container);
});
runner.it('should support document status tracking', () => {
const SectionManager = global.ExtractedSectionManager;
const DOMRenderer = global.ExtractedDOMRenderer;
const sectionManager = new SectionManager();
const container = document.createElement('div');
const domRenderer = new DOMRenderer(sectionManager, container);
// Test initial status
let status = sectionManager.getDocumentStatus();
runner.expect(status.totalSections).toBe(0);
runner.expect(status.editingSections).toBe(0);
// Create sections
const testMarkdown = '# Section 1\nContent 1\n\n# Section 2\nContent 2';
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
status = sectionManager.getDocumentStatus();
runner.expect(status.totalSections).toBe(2);
runner.expect(status.editingSections).toBe(2); // Bug compatibility (isEditing property exists)
// Test getAllSections
const allSections = sectionManager.getAllSections();
runner.expect(allSections.length).toBe(2);
runner.expect(allSections[0].currentMarkdown).toContain('Section 1');
runner.expect(allSections[1].currentMarkdown).toContain('Section 2');
});
runner.it('should support event tracking and analytics', () => {
const SectionManager = global.ExtractedSectionManager;
const DOMRenderer = global.ExtractedDOMRenderer;
const container = document.createElement('div');
const sectionManager = new SectionManager();
const domRenderer = new DOMRenderer(sectionManager, container);
// Test event tracking
domRenderer.trackEvent('test-event', { data: 'test' });
domRenderer.trackEvent('section-click', { sectionId: 'test-123' });
const stats = domRenderer.getEventStats();
runner.expect(stats.totalEvents).toBe(1); // Only section-click is tracked in stats
runner.expect(stats.stats['section-click']).toBe(1);
runner.expect(stats.recentEvents.length).toBe(2);
runner.expect(stats.recentEvents[0].type).toBe('test-event');
runner.expect(stats.recentEvents[1].type).toBe('section-click');
});
// Integration stress test
runner.it('should handle complex document with multiple operations', () => {
const SectionManager = global.ExtractedSectionManager;
const DOMRenderer = global.ExtractedDOMRenderer;
// Setup
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
document.body.appendChild(container);
const sectionManager = new SectionManager();
const domRenderer = new DOMRenderer(sectionManager, container);
// Complex document
const complexMarkdown = `# Document Title
Introduction paragraph with some content.
## Section A
Content for section A with details.
![Test Image](https://example.com/test.jpg)
### Subsection A.1
More detailed content here.
\`\`\`javascript
function test() {
console.log('code block');
}
\`\`\`
## Section B
Final section content.`;
// Create and render
const sections = sectionManager.createSectionsFromMarkdown(complexMarkdown);
domRenderer.renderAllSections(sections);
runner.expect(sections.length).toBe(6); // Adjusted based on actual parsing
// Test editing multiple sections
const firstSection = sections[0];
const imageSection = sections.find(s => s.isImage());
const codeSection = sections.find(s => s.type === 'code');
// Edit first section
sectionManager.startEditing(firstSection.id);
sectionManager.updateContent(firstSection.id, '# Updated Title\nUpdated intro.');
sectionManager.acceptChanges(firstSection.id);
// Edit image section
sectionManager.startEditing(imageSection.id);
sectionManager.updateContent(imageSection.id, '![Updated Image](https://example.com/new.jpg)');
sectionManager.acceptChanges(imageSection.id);
// Verify changes
runner.expect(firstSection.currentMarkdown).toContain('Updated Title');
runner.expect(imageSection.currentMarkdown).toContain('Updated Image');
// Verify document reconstruction
const finalMarkdown = sectionManager.getDocumentMarkdown();
runner.expect(finalMarkdown).toContain('Updated Title');
runner.expect(finalMarkdown).toContain('Updated Image');
runner.expect(finalMarkdown).toContain('Section B');
// Cleanup
document.body.removeChild(container);
});
});
module.exports = runner;
// Run tests if called directly
if (require.main === module) {
console.log('🧪 Running Component Integration Tests');
runner.run().then(() => {
console.log('✅ Component integration tests completed');
});
}

View File

@@ -1,191 +0,0 @@
#!/usr/bin/env node
/**
* TDD Test for Debug Panel Component Extraction
*
* Tests the extraction of DebugPanel from the monolithic editor.js
* DebugPanel handles debug message display and management.
*/
const RefactorTestRunner = require('./refactor-test-runner.js');
const runner = new RefactorTestRunner();
// Define expected DebugPanel API
const EXPECTED_DEBUGPANEL_API = [
'constructor',
'toggle',
'update',
'clear',
'addMessage',
'show',
'hide',
'getMessageCount',
'getRecentMessages'
];
runner.describe('DebugPanel Component Extraction', () => {
runner.it('should define expected API methods', () => {
const expectedMethods = EXPECTED_DEBUGPANEL_API;
runner.expect(expectedMethods.length).toBe(9);
runner.expect(expectedMethods).toContain('toggle');
runner.expect(expectedMethods).toContain('update');
runner.expect(expectedMethods).toContain('addMessage');
});
runner.it('should load extracted DebugPanel component', () => {
// Load the extracted component
delete require.cache[require.resolve('../components/debug-panel.js')];
try {
const module = require('../components/debug-panel.js');
runner.expect(module.DebugPanel).toBeTruthy();
// Set global for other tests
global.ExtractedDebugPanel = module.DebugPanel;
} catch (error) {
throw new Error(`Failed to load extracted DebugPanel: ${error.message}`);
}
});
runner.it('should preserve constructor functionality', () => {
const DebugPanel = global.ExtractedDebugPanel;
const debugPanel = new DebugPanel();
runner.expect(debugPanel).toBeInstanceOf(DebugPanel);
runner.expect(debugPanel.messages).toBeInstanceOf(Array);
runner.expect(debugPanel.isActive).toBeFalsy();
});
runner.it('should preserve message handling functionality', () => {
const DebugPanel = global.ExtractedDebugPanel;
const debugPanel = new DebugPanel();
// Test adding messages
debugPanel.addMessage('Test message', 'INFO');
runner.expect(debugPanel.getMessageCount()).toBe(1);
const recentMessages = debugPanel.getRecentMessages(1);
runner.expect(recentMessages.length).toBe(1);
runner.expect(recentMessages[0].message).toBe('Test message');
runner.expect(recentMessages[0].category).toBe('INFO');
});
runner.it('should preserve toggle functionality', () => {
const DebugPanel = global.ExtractedDebugPanel;
// Create container element
const container = document.createElement('div');
container.id = 'debug-messages-container';
container.style.display = 'none';
document.body.appendChild(container);
const debugButton = document.createElement('button');
debugButton.id = 'toggle-debug';
debugButton.textContent = '🔍 Debug';
document.body.appendChild(debugButton);
const debugPanel = new DebugPanel();
// Test toggle on
debugPanel.toggle();
runner.expect(debugPanel.isActive).toBeTruthy();
// Test toggle off
debugPanel.toggle();
runner.expect(debugPanel.isActive).toBeFalsy();
// Cleanup
document.body.removeChild(container);
document.body.removeChild(debugButton);
});
runner.it('should preserve update functionality', () => {
const DebugPanel = global.ExtractedDebugPanel;
const container = document.createElement('div');
container.id = 'debug-messages-container';
document.body.appendChild(container);
const debugButton = document.createElement('button');
debugButton.id = 'toggle-debug';
debugButton.textContent = '🔍 Debug';
document.body.appendChild(debugButton);
const debugPanel = new DebugPanel();
debugPanel.show();
debugPanel.addMessage('Test message 1', 'INFO');
debugPanel.addMessage('Test message 2', 'ERROR');
debugPanel.update();
runner.expect(container.innerHTML.length > 100).toBeTruthy();
runner.expect(container.innerHTML).toContain('Test message 1');
runner.expect(container.innerHTML).toContain('Test message 2');
// Cleanup
document.body.removeChild(container);
document.body.removeChild(debugButton);
});
runner.it('should preserve clear functionality', () => {
const DebugPanel = global.ExtractedDebugPanel;
const debugPanel = new DebugPanel();
debugPanel.addMessage('Test message 1', 'INFO');
debugPanel.addMessage('Test message 2', 'ERROR');
runner.expect(debugPanel.getMessageCount()).toBe(2);
debugPanel.clear();
runner.expect(debugPanel.getMessageCount()).toBe(0);
});
runner.it('should have core debug panel methods', () => {
const DebugPanel = global.ExtractedDebugPanel;
const debugPanel = new DebugPanel();
// Should have core methods
runner.expect(typeof debugPanel.toggle === 'function').toBeTruthy();
runner.expect(typeof debugPanel.update === 'function').toBeTruthy();
runner.expect(typeof debugPanel.addMessage === 'function').toBeTruthy();
runner.expect(typeof debugPanel.clear === 'function').toBeTruthy();
});
runner.it('should handle message categories properly', () => {
const DebugPanel = global.ExtractedDebugPanel;
const debugPanel = new DebugPanel();
// Test different message categories
debugPanel.addMessage('Info message', 'INFO');
debugPanel.addMessage('Warning message', 'WARNING');
debugPanel.addMessage('Error message', 'ERROR');
debugPanel.addMessage('Success message', 'SUCCESS');
const messages = debugPanel.getRecentMessages(4);
runner.expect(messages.length).toBe(4);
const categories = messages.map(m => m.category);
runner.expect(categories).toContain('INFO');
runner.expect(categories).toContain('WARNING');
runner.expect(categories).toContain('ERROR');
runner.expect(categories).toContain('SUCCESS');
});
});
module.exports = {
runner,
EXPECTED_DEBUGPANEL_API
};
// Run tests if called directly
if (require.main === module) {
console.log('🧪 Testing DebugPanel Component Extraction');
runner.run().then(() => {
console.log('✅ DebugPanel extraction tests completed');
});
}

View File

@@ -1,210 +0,0 @@
#!/usr/bin/env node
/**
* DebugPanel Integration Test
*
* Tests that the extracted DebugPanel component integrates properly
* with the existing SectionManager and DOMRenderer components.
*/
const RefactorTestRunner = require('./refactor-test-runner.js');
const runner = new RefactorTestRunner();
runner.describe('DebugPanel Integration Tests', () => {
runner.it('should load all extracted components including DebugPanel', () => {
try {
// Load extracted components
const sectionModule = require('../core/section-manager.js');
const domModule = require('../components/dom-renderer.js');
const debugModule = require('../components/debug-panel.js');
runner.expect(sectionModule.SectionManager).toBeTruthy();
runner.expect(domModule.DOMRenderer).toBeTruthy();
runner.expect(debugModule.DebugPanel).toBeTruthy();
// Set globals for other tests
global.ExtractedSectionManager = sectionModule.SectionManager;
global.ExtractedDOMRenderer = domModule.DOMRenderer;
global.ExtractedDebugPanel = debugModule.DebugPanel;
} catch (error) {
throw new Error(`Failed to load extracted components: ${error.message}`);
}
});
runner.it('should support debug panel with section editing workflow', () => {
const SectionManager = global.ExtractedSectionManager;
const DOMRenderer = global.ExtractedDOMRenderer;
const DebugPanel = global.ExtractedDebugPanel;
// Setup DOM elements
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
document.body.appendChild(container);
const debugContainer = document.createElement('div');
debugContainer.id = 'debug-messages-container';
debugContainer.style.display = 'none';
document.body.appendChild(debugContainer);
const debugButton = document.createElement('button');
debugButton.id = 'toggle-debug';
debugButton.textContent = '🔍 Debug';
document.body.appendChild(debugButton);
// Create components
const sectionManager = new SectionManager();
const domRenderer = new DOMRenderer(sectionManager, container);
const debugPanel = new DebugPanel();
// Test workflow: Create sections and debug them
const testMarkdown = '# Test Heading\nTest content for debugging';
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
domRenderer.renderAllSections(sections);
// Add debug messages
debugPanel.addMessage('Section created: ' + sections[0].id, 'INFO');
debugPanel.addMessage('DOM rendered successfully', 'SUCCESS');
runner.expect(debugPanel.getMessageCount()).toBe(2);
// Test showing debug panel
debugPanel.show();
runner.expect(debugPanel.isActive).toBeTruthy();
// Test debug panel content
const messages = debugPanel.getRecentMessages(2);
runner.expect(messages[0].message).toContain('Section created');
runner.expect(messages[1].message).toContain('DOM rendered');
// Cleanup
document.body.removeChild(container);
document.body.removeChild(debugContainer);
document.body.removeChild(debugButton);
});
runner.it('should support debug panel clearing and message management', () => {
const DebugPanel = global.ExtractedDebugPanel;
const debugPanel = new DebugPanel();
// Add multiple messages
for (let i = 0; i < 10; i++) {
debugPanel.addMessage(`Test message ${i}`, i % 2 === 0 ? 'INFO' : 'WARNING');
}
runner.expect(debugPanel.getMessageCount()).toBe(10);
// Test getting recent messages
const recentFive = debugPanel.getRecentMessages(5);
runner.expect(recentFive.length).toBe(5);
runner.expect(recentFive[4].message).toContain('Test message 9');
// Test clearing
debugPanel.clear();
runner.expect(debugPanel.getMessageCount()).toBe(0);
});
runner.it('should handle debug panel DOM integration properly', () => {
const DebugPanel = global.ExtractedDebugPanel;
// Setup DOM
const debugContainer = document.createElement('div');
debugContainer.id = 'debug-messages-container';
debugContainer.style.display = 'none';
document.body.appendChild(debugContainer);
const debugButton = document.createElement('button');
debugButton.id = 'toggle-debug';
debugButton.textContent = '🔍 Debug';
debugButton.style.background = '#6c757d';
document.body.appendChild(debugButton);
const debugPanel = new DebugPanel();
// Test initial state
runner.expect(debugPanel.isActive).toBeFalsy();
runner.expect(debugContainer.style.display).toBe('none');
// Test toggle on
debugPanel.toggle();
runner.expect(debugPanel.isActive).toBeTruthy();
runner.expect(debugContainer.style.display).toBe('block');
runner.expect(debugButton.textContent).toContain('Debug (ON)');
// Test toggle off
debugPanel.toggle();
runner.expect(debugPanel.isActive).toBeFalsy();
runner.expect(debugContainer.style.display).toBe('none');
runner.expect(debugButton.textContent).toBe('🔍 Debug');
// Cleanup
document.body.removeChild(debugContainer);
document.body.removeChild(debugButton);
});
runner.it('should handle missing DOM elements gracefully', () => {
const DebugPanel = global.ExtractedDebugPanel;
const debugPanel = new DebugPanel();
// Try to toggle without DOM elements (should not throw)
try {
debugPanel.toggle();
debugPanel.show();
debugPanel.hide();
debugPanel.update();
runner.expect(true).toBeTruthy(); // If we get here, no errors were thrown
} catch (error) {
throw new Error(`DebugPanel should handle missing DOM gracefully: ${error.message}`);
}
});
runner.it('should support event-driven debug message addition', () => {
const SectionManager = global.ExtractedSectionManager;
const DebugPanel = global.ExtractedDebugPanel;
const sectionManager = new SectionManager();
const debugPanel = new DebugPanel();
// Listen to section manager events and add debug messages
let eventCount = 0;
sectionManager.on('sections-created', (data) => {
debugPanel.addMessage(`Sections created: ${data.count} sections`, 'INFO');
eventCount++;
});
sectionManager.on('edit-started', (data) => {
debugPanel.addMessage(`Edit started for section: ${data.sectionId}`, 'DEBUG');
eventCount++;
});
// Create sections
const testMarkdown = '# Test\nContent';
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
// Start editing
sectionManager.startEditing(sections[0].id);
// Verify debug messages were added
runner.expect(eventCount).toBe(2);
runner.expect(debugPanel.getMessageCount()).toBe(2);
const messages = debugPanel.getRecentMessages(2);
runner.expect(messages[0].message).toContain('Sections created');
runner.expect(messages[1].message).toContain('Edit started');
});
});
module.exports = runner;
// Run tests if called directly
if (require.main === module) {
console.log('🧪 Running DebugPanel Integration Tests');
runner.run().then(() => {
console.log('✅ DebugPanel integration tests completed');
});
}

View File

@@ -1,218 +0,0 @@
#!/usr/bin/env node
/**
* TDD Test for Document Controls Component Extraction
*
* Tests the extraction of DocumentControls from the monolithic editor.js
* DocumentControls handles the floating control panel and its actions.
*/
const RefactorTestRunner = require('./refactor-test-runner.js');
const runner = new RefactorTestRunner();
// Define expected DocumentControls API
const EXPECTED_DOCUMENTCONTROLS_API = [
'constructor',
'create',
'destroy',
'show',
'hide',
'addButton',
'removeButton',
'setEventHandlers',
'updateStatus',
'getControlPanel'
];
runner.describe('DocumentControls Component Extraction', () => {
runner.it('should define expected API methods', () => {
const expectedMethods = EXPECTED_DOCUMENTCONTROLS_API;
runner.expect(expectedMethods.length).toBe(10);
runner.expect(expectedMethods).toContain('create');
runner.expect(expectedMethods).toContain('addButton');
runner.expect(expectedMethods).toContain('setEventHandlers');
});
runner.it('should load extracted DocumentControls component', () => {
// Load the extracted component
delete require.cache[require.resolve('../components/document-controls.js')];
try {
const module = require('../components/document-controls.js');
runner.expect(module.DocumentControls).toBeTruthy();
// Set global for other tests
global.ExtractedDocumentControls = module.DocumentControls;
} catch (error) {
throw new Error(`Failed to load extracted DocumentControls: ${error.message}`);
}
});
runner.it('should preserve constructor functionality', () => {
const DocumentControls = global.ExtractedDocumentControls;
const controls = new DocumentControls();
runner.expect(controls).toBeInstanceOf(DocumentControls);
runner.expect(controls.controlPanel).toBeFalsy(); // Initially null
runner.expect(controls.buttons).toBeInstanceOf(Map);
});
runner.it('should preserve control panel creation functionality', () => {
const DocumentControls = global.ExtractedDocumentControls;
const controls = new DocumentControls();
controls.create();
const panel = controls.getControlPanel();
runner.expect(panel).toBeTruthy();
runner.expect(panel.id).toBe('markitect-global-controls');
// Check that panel is added to DOM
const domPanel = document.getElementById('markitect-global-controls');
runner.expect(domPanel).toBeTruthy();
// Cleanup
controls.destroy();
});
runner.it('should preserve button creation functionality', () => {
const DocumentControls = global.ExtractedDocumentControls;
const controls = new DocumentControls();
controls.create();
// Default buttons should be created
runner.expect(controls.buttons.has('save-document')).toBeTruthy();
runner.expect(controls.buttons.has('reset-all')).toBeTruthy();
runner.expect(controls.buttons.has('show-status')).toBeTruthy();
runner.expect(controls.buttons.has('toggle-debug')).toBeTruthy();
// Check DOM elements exist
runner.expect(document.getElementById('save-document')).toBeTruthy();
runner.expect(document.getElementById('reset-all')).toBeTruthy();
runner.expect(document.getElementById('show-status')).toBeTruthy();
runner.expect(document.getElementById('toggle-debug')).toBeTruthy();
// Cleanup
controls.destroy();
});
runner.it('should support custom button addition', () => {
const DocumentControls = global.ExtractedDocumentControls;
const controls = new DocumentControls();
controls.create();
// Add custom button
const customButton = controls.addButton('custom-test', '🎯 Test', '#ff6600');
runner.expect(customButton).toBeTruthy();
runner.expect(customButton.id).toBe('custom-test');
runner.expect(customButton.textContent).toBe('🎯 Test');
// Check button is in map and DOM
runner.expect(controls.buttons.has('custom-test')).toBeTruthy();
runner.expect(document.getElementById('custom-test')).toBeTruthy();
// Cleanup
controls.destroy();
});
runner.it('should support event handler configuration', () => {
const DocumentControls = global.ExtractedDocumentControls;
const controls = new DocumentControls();
controls.create();
let saveClicked = false;
let resetClicked = false;
const handlers = {
'save-document': () => { saveClicked = true; },
'reset-all': () => { resetClicked = true; }
};
controls.setEventHandlers(handlers);
// Simulate button clicks
const saveBtn = document.getElementById('save-document');
const resetBtn = document.getElementById('reset-all');
saveBtn.click();
resetBtn.click();
runner.expect(saveClicked).toBeTruthy();
runner.expect(resetClicked).toBeTruthy();
// Cleanup
controls.destroy();
});
runner.it('should support show/hide functionality', () => {
const DocumentControls = global.ExtractedDocumentControls;
const controls = new DocumentControls();
controls.create();
const panel = controls.getControlPanel();
// Test hiding
controls.hide();
runner.expect(panel.style.display).toBe('none');
// Test showing
controls.show();
runner.expect(panel.style.display).toBe('block');
// Cleanup
controls.destroy();
});
runner.it('should preserve destroy functionality', () => {
const DocumentControls = global.ExtractedDocumentControls;
const controls = new DocumentControls();
controls.create();
// Verify panel exists
runner.expect(document.getElementById('markitect-global-controls')).toBeTruthy();
// Destroy
controls.destroy();
// Verify panel is removed
runner.expect(document.getElementById('markitect-global-controls')).toBeFalsy();
runner.expect(controls.controlPanel).toBeFalsy();
});
runner.it('should support status updates', () => {
const DocumentControls = global.ExtractedDocumentControls;
const controls = new DocumentControls();
controls.create();
// Test status update
controls.updateStatus({ totalSections: 5, editingSections: 2 });
// The status should be reflected in the panel (implementation specific)
const panel = controls.getControlPanel();
runner.expect(panel).toBeTruthy();
// Cleanup
controls.destroy();
});
});
module.exports = {
runner,
EXPECTED_DOCUMENTCONTROLS_API
};
// Run tests if called directly
if (require.main === module) {
console.log('🧪 Testing DocumentControls Component Extraction');
runner.run().then(() => {
console.log('✅ DocumentControls extraction tests completed');
});
}

View File

@@ -1,212 +0,0 @@
#!/usr/bin/env node
/**
* TDD Test for DOMRenderer Component Extraction
*
* Tests the extraction of DOMRenderer from the monolithic editor.js
* DOMRenderer handles all DOM interactions and UI rendering.
*/
const RefactorTestRunner = require('./refactor-test-runner.js');
const runner = new RefactorTestRunner();
// Define expected DOMRenderer API
const EXPECTED_DOMRENDERER_API = [
'constructor',
'renderAllSections',
'renderSection',
'showEditor',
'hideCurrentEditor',
'showImageEditor',
'findSectionElement',
'handleSectionClick',
'setupSectionElement',
'trackEvent',
'getEventStats'
// Note: addGlobalControls and debug methods are on MarkitectCleanEditor, not DOMRenderer
];
runner.describe('DOMRenderer Component Extraction', () => {
runner.it('should define expected API methods', () => {
const expectedMethods = EXPECTED_DOMRENDERER_API;
runner.expect(expectedMethods.length).toBe(11);
runner.expect(expectedMethods).toContain('renderAllSections');
runner.expect(expectedMethods).toContain('showEditor');
runner.expect(expectedMethods).toContain('handleSectionClick');
});
runner.it('should extract from monolithic editor.js', () => {
// Load the monolithic editor.js to extract DOMRenderer
delete require.cache[require.resolve('/home/worsch/markitect_project/markitect/static/editor.js')];
try {
const editorModule = require('/home/worsch/markitect_project/markitect/static/editor.js');
runner.expect(editorModule.DOMRenderer).toBeTruthy();
// Set global for other tests
global.DOMRenderer = editorModule.DOMRenderer;
global.SectionManager = editorModule.SectionManager;
} catch (error) {
throw new Error(`Failed to load monolithic editor.js: ${error.message}`);
}
});
runner.it('should preserve DOMRenderer constructor functionality', () => {
const DOMRenderer = global.DOMRenderer;
const SectionManager = global.SectionManager;
const container = document.createElement('div');
const sectionManager = new SectionManager();
const renderer = new DOMRenderer(sectionManager, container);
runner.expect(renderer).toBeInstanceOf(DOMRenderer);
runner.expect(renderer.sectionManager).toBe(sectionManager);
runner.expect(renderer.container).toBe(container);
});
runner.it('should preserve section rendering functionality', () => {
const DOMRenderer = global.DOMRenderer;
const SectionManager = global.SectionManager;
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
const sectionManager = new SectionManager();
const renderer = new DOMRenderer(sectionManager, container);
const testMarkdown = '# Test Heading\nTest content';
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
// This should not throw an error
renderer.renderAllSections(sections);
// Check that some content was rendered
runner.expect(container.innerHTML.length).toBe(container.innerHTML.length); // Basic sanity check
});
runner.it('should preserve findSectionElement functionality', () => {
const DOMRenderer = global.DOMRenderer;
const SectionManager = global.SectionManager;
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
const sectionManager = new SectionManager();
const renderer = new DOMRenderer(sectionManager, container);
const testMarkdown = '# Test Heading\nTest content';
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
renderer.renderAllSections(sections);
const sectionId = sections[0].id;
const element = renderer.findSectionElement(sectionId);
// Should find an element or return null (not throw error)
runner.expect(typeof element === 'object').toBeTruthy();
});
runner.it('should preserve event tracking functionality', () => {
const DOMRenderer = global.DOMRenderer;
const SectionManager = global.SectionManager;
const container = document.createElement('div');
const sectionManager = new SectionManager();
const renderer = new DOMRenderer(sectionManager, container);
// Should have trackEvent method
runner.expect(typeof renderer.trackEvent === 'function').toBeTruthy();
// Should be able to track an event
renderer.trackEvent('test-event', { data: 'test' });
// Should have getEventStats method
runner.expect(typeof renderer.getEventStats === 'function').toBeTruthy();
const stats = renderer.getEventStats();
runner.expect(typeof stats === 'object').toBeTruthy();
});
runner.it('should preserve editor showing functionality', () => {
const DOMRenderer = global.DOMRenderer;
const SectionManager = global.SectionManager;
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
const sectionManager = new SectionManager();
const renderer = new DOMRenderer(sectionManager, container);
const testMarkdown = '# Test Heading\nTest content';
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
renderer.renderAllSections(sections);
const sectionId = sections[0].id;
// showEditor should not throw error
try {
renderer.showEditor(sectionId, 'test content');
runner.expect(true).toBeTruthy(); // If we get here, no error was thrown
} catch (error) {
// Some errors are expected if DOM structure isn't complete
runner.expect(typeof error.message === 'string').toBeTruthy();
}
});
runner.it('should have core DOM rendering methods', () => {
const DOMRenderer = global.DOMRenderer;
const SectionManager = global.SectionManager;
const container = document.createElement('div');
const sectionManager = new SectionManager();
const renderer = new DOMRenderer(sectionManager, container);
// Should have core methods
runner.expect(typeof renderer.renderAllSections === 'function').toBeTruthy();
runner.expect(typeof renderer.showEditor === 'function').toBeTruthy();
runner.expect(typeof renderer.findSectionElement === 'function').toBeTruthy();
runner.expect(typeof renderer.trackEvent === 'function').toBeTruthy();
});
});
// Export API tests for use during extraction
const DOMRENDERER_API_TESTS = [
(DOMRenderer, SectionManager) => {
const container = document.createElement('div');
const sectionManager = new SectionManager();
const renderer = new DOMRenderer(sectionManager, container);
if (!renderer.sectionManager) {
throw new Error('sectionManager property missing');
}
},
(DOMRenderer, SectionManager) => {
const container = document.createElement('div');
const sectionManager = new SectionManager();
const renderer = new DOMRenderer(sectionManager, container);
if (typeof renderer.renderAllSections !== 'function') {
throw new Error('renderAllSections method missing');
}
},
(DOMRenderer, SectionManager) => {
const container = document.createElement('div');
const sectionManager = new SectionManager();
const renderer = new DOMRenderer(sectionManager, container);
if (typeof renderer.showEditor !== 'function') {
throw new Error('showEditor method missing');
}
}
];
module.exports = {
runner,
EXPECTED_DOMRENDERER_API,
DOMRENDERER_API_TESTS
};
// Run tests if called directly
if (require.main === module) {
console.log('🧪 Testing DOMRenderer Component Extraction');
runner.run().then(() => {
console.log('✅ DOMRenderer extraction tests completed');
});
}

View File

@@ -1,24 +0,0 @@
/**
* Environment Test - Verifies Jest setup is working correctly
*/
describe('Test Environment', () => {
test('should have JSDOM environment available', () => {
expect(global.document).toBeDefined();
expect(global.window).toBeDefined();
expect(document.createElement).toBeDefined();
});
test('should be able to create DOM elements', () => {
const div = document.createElement('div');
div.textContent = 'Test content';
expect(div.tagName).toBe('DIV');
expect(div.textContent).toBe('Test content');
});
test('should have content container available', () => {
const contentEl = document.getElementById('content');
expect(contentEl).toBeDefined();
expect(contentEl.tagName).toBe('DIV');
});
});

View File

@@ -1,271 +0,0 @@
#!/usr/bin/env node
/**
* TDD Test for Extracted DOMRenderer Component
*
* Tests the extracted DOMRenderer component independently from the monolith.
* Verifies that core functionality is preserved after extraction.
*/
const RefactorTestRunner = require('./refactor-test-runner.js');
const runner = new RefactorTestRunner();
runner.describe('Extracted DOMRenderer Component', () => {
runner.it('should load extracted DOMRenderer component', () => {
// Load the extracted component
delete require.cache[require.resolve('../components/dom-renderer.js')];
try {
const module = require('../components/dom-renderer.js');
runner.expect(module.DOMRenderer).toBeTruthy();
runner.expect(module.FloatingMenu).toBeTruthy();
// Set globals for other tests
global.ExtractedDOMRenderer = module.DOMRenderer;
global.ExtractedFloatingMenu = module.FloatingMenu;
} catch (error) {
throw new Error(`Failed to load extracted DOMRenderer: ${error.message}`);
}
});
runner.it('should preserve constructor functionality', () => {
const DOMRenderer = global.ExtractedDOMRenderer;
// Load SectionManager from our extracted core
const sectionModule = require('../core/section-manager.js');
const SectionManager = sectionModule.SectionManager;
const container = document.createElement('div');
const sectionManager = new SectionManager();
const renderer = new DOMRenderer(sectionManager, container);
runner.expect(renderer).toBeInstanceOf(DOMRenderer);
runner.expect(renderer.sectionManager).toBe(sectionManager);
runner.expect(renderer.container).toBe(container);
runner.expect(renderer.editingSections).toBeInstanceOf(Set);
});
runner.it('should preserve section rendering functionality', () => {
const DOMRenderer = global.ExtractedDOMRenderer;
const sectionModule = require('../core/section-manager.js');
const SectionManager = sectionModule.SectionManager;
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
const sectionManager = new SectionManager();
const renderer = new DOMRenderer(sectionManager, container);
const testMarkdown = '# Test Heading\nTest content';
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
// This should not throw an error
renderer.renderAllSections(sections);
// Check that content was rendered
runner.expect(container.innerHTML.length > 100).toBeTruthy();
runner.expect(container.innerHTML).toContain('Test Heading');
});
runner.it('should preserve findSectionElement functionality', () => {
const DOMRenderer = global.ExtractedDOMRenderer;
const sectionModule = require('../core/section-manager.js');
const SectionManager = sectionModule.SectionManager;
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
const sectionManager = new SectionManager();
const renderer = new DOMRenderer(sectionManager, container);
const testMarkdown = '# Test Heading\nTest content';
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
renderer.renderAllSections(sections);
const sectionId = sections[0].id;
const element = renderer.findSectionElement(sectionId);
runner.expect(element).toBeTruthy();
runner.expect(element.getAttribute('data-section-id')).toBe(sectionId);
});
runner.it('should preserve event tracking functionality', () => {
const DOMRenderer = global.ExtractedDOMRenderer;
const sectionModule = require('../core/section-manager.js');
const SectionManager = sectionModule.SectionManager;
const container = document.createElement('div');
const sectionManager = new SectionManager();
const renderer = new DOMRenderer(sectionManager, container);
// Should have trackEvent method
runner.expect(typeof renderer.trackEvent === 'function').toBeTruthy();
// Should be able to track an event
renderer.trackEvent('test-event', { data: 'test' });
// Should have getEventStats method
runner.expect(typeof renderer.getEventStats === 'function').toBeTruthy();
const stats = renderer.getEventStats();
runner.expect(typeof stats === 'object').toBeTruthy();
runner.expect(stats).toHaveProperty('stats');
runner.expect(stats).toHaveProperty('totalEvents');
runner.expect(stats).toHaveProperty('recentEvents');
});
runner.it('should preserve editor showing functionality', () => {
const DOMRenderer = global.ExtractedDOMRenderer;
const sectionModule = require('../core/section-manager.js');
const SectionManager = sectionModule.SectionManager;
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
const sectionManager = new SectionManager();
const renderer = new DOMRenderer(sectionManager, container);
const testMarkdown = '# Test Heading\nTest content';
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
renderer.renderAllSections(sections);
const sectionId = sections[0].id;
// showEditor should not throw error
try {
renderer.showEditor(sectionId, 'test content');
runner.expect(true).toBeTruthy(); // If we get here, no error was thrown
// Check that editing state was set
runner.expect(renderer.editingSections.has(sectionId)).toBeTruthy();
} catch (error) {
throw new Error(`showEditor failed: ${error.message}`);
}
});
runner.it('should preserve FloatingMenu functionality', () => {
const FloatingMenu = global.ExtractedFloatingMenu;
const DOMRenderer = global.ExtractedDOMRenderer;
const sectionModule = require('../core/section-manager.js');
const SectionManager = sectionModule.SectionManager;
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
const sectionManager = new SectionManager();
const renderer = new DOMRenderer(sectionManager, container);
const testMarkdown = '# Test Heading\nTest content';
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
renderer.renderAllSections(sections);
const sectionId = sections[0].id;
const floatingMenu = new FloatingMenu(sectionId, 'text', renderer);
runner.expect(floatingMenu.sectionId).toBe(sectionId);
runner.expect(floatingMenu.type).toBe('text');
runner.expect(floatingMenu.renderer).toBe(renderer);
runner.expect(floatingMenu.isVisible).toBeFalsy();
// Test show/hide functionality
const content = document.createElement('div');
content.textContent = 'Test content';
floatingMenu.show(content);
runner.expect(floatingMenu.isVisible).toBeTruthy();
floatingMenu.hide();
runner.expect(floatingMenu.isVisible).toBeFalsy();
});
runner.it('should handle section click events', () => {
const DOMRenderer = global.ExtractedDOMRenderer;
const sectionModule = require('../core/section-manager.js');
const SectionManager = sectionModule.SectionManager;
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
const sectionManager = new SectionManager();
const renderer = new DOMRenderer(sectionManager, container);
const testMarkdown = '# Test Heading\nTest content';
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
renderer.renderAllSections(sections);
const sectionId = sections[0].id;
const element = renderer.findSectionElement(sectionId);
// Simulate a click event
const clickEvent = new Event('click', { bubbles: true });
Object.defineProperty(clickEvent, 'target', { value: element });
// Should not throw error
try {
renderer.handleSectionClick(clickEvent);
runner.expect(true).toBeTruthy();
} catch (error) {
throw new Error(`handleSectionClick failed: ${error.message}`);
}
});
// Comparative test - verify extracted component behaves similarly to original
runner.it('should behave similarly to original monolithic component', () => {
// Load both components
const originalModule = require('/home/worsch/markitect_project/markitect/static/editor.js');
const extractedModule = require('../components/dom-renderer.js');
const sectionModule = require('../core/section-manager.js');
const originalSectionManager = new originalModule.SectionManager();
const extractedSectionManager = new sectionModule.SectionManager();
const originalContainer = document.createElement('div');
originalContainer.innerHTML = '<div id="markdown-content"></div>';
const extractedContainer = document.createElement('div');
extractedContainer.innerHTML = '<div id="markdown-content"></div>';
const originalRenderer = new originalModule.DOMRenderer(originalSectionManager, originalContainer);
const extractedRenderer = new extractedModule.DOMRenderer(extractedSectionManager, extractedContainer);
const testMarkdown = '# Test\nContent\n\n## Subheading\nMore content';
// Create sections with both
const originalSections = originalSectionManager.createSectionsFromMarkdown(testMarkdown);
const extractedSections = extractedSectionManager.createSectionsFromMarkdown(testMarkdown);
// Render with both
originalRenderer.renderAllSections(originalSections);
extractedRenderer.renderAllSections(extractedSections);
// Should have rendered content
runner.expect(originalContainer.innerHTML.length > 100).toBeTruthy();
runner.expect(extractedContainer.innerHTML.length > 100).toBeTruthy();
// Should have same number of section elements
const originalSectionElements = originalContainer.querySelectorAll('.ui-edit-section');
const extractedSectionElements = extractedContainer.querySelectorAll('.ui-edit-section');
runner.expect(extractedSectionElements.length).toBe(originalSectionElements.length);
// Should have similar event stats structure
const originalStats = originalRenderer.getEventStats();
const extractedStats = extractedRenderer.getEventStats();
runner.expect(extractedStats).toHaveProperty('stats');
runner.expect(extractedStats).toHaveProperty('totalEvents');
runner.expect(extractedStats).toHaveProperty('recentEvents');
});
});
module.exports = runner;
// Run tests if called directly
if (require.main === module) {
console.log('🧪 Testing Extracted DOMRenderer Component');
runner.run().then(() => {
console.log('✅ Extracted DOMRenderer tests completed');
});
}

View File

@@ -1,226 +0,0 @@
#!/usr/bin/env node
/**
* TDD Test for Extracted SectionManager Component
*
* Tests the extracted SectionManager component independently from the monolith.
* Verifies that all functionality is preserved after extraction.
*/
const RefactorTestRunner = require('./refactor-test-runner.js');
const runner = new RefactorTestRunner();
runner.describe('Extracted SectionManager Component', () => {
runner.it('should load extracted SectionManager component', () => {
// Load the extracted component
delete require.cache[require.resolve('../core/section-manager.js')];
try {
const module = require('../core/section-manager.js');
runner.expect(module.SectionManager).toBeTruthy();
runner.expect(module.Section).toBeTruthy();
runner.expect(module.EditState).toBeTruthy();
runner.expect(module.SectionType).toBeTruthy();
// Set globals for other tests
global.ExtractedSectionManager = module.SectionManager;
global.ExtractedSection = module.Section;
global.ExtractedEditState = module.EditState;
global.ExtractedSectionType = module.SectionType;
} catch (error) {
throw new Error(`Failed to load extracted SectionManager: ${error.message}`);
}
});
runner.it('should preserve constructor functionality', () => {
const SectionManager = global.ExtractedSectionManager;
const manager = new SectionManager();
runner.expect(manager).toBeInstanceOf(SectionManager);
runner.expect(manager.sections).toBeInstanceOf(Map);
runner.expect(manager.listeners).toBeInstanceOf(Map);
});
runner.it('should preserve section creation functionality', () => {
const SectionManager = global.ExtractedSectionManager;
const manager = new SectionManager();
const testMarkdown = `# Heading 1\nContent 1\n\n## Heading 2\nContent 2`;
const sections = manager.createSectionsFromMarkdown(testMarkdown);
runner.expect(Array.isArray(sections)).toBeTruthy();
runner.expect(sections.length).toBe(2);
runner.expect(sections[0].currentMarkdown).toContain('Heading 1');
runner.expect(sections[1].currentMarkdown).toContain('Heading 2');
});
runner.it('should preserve section editing functionality', () => {
const SectionManager = global.ExtractedSectionManager;
const manager = new SectionManager();
const sections = manager.createSectionsFromMarkdown('# Test\nContent');
const sectionId = sections[0].id;
// Test start editing
const content = manager.startEditing(sectionId);
runner.expect(content).toContain('Test');
const section = manager.sections.get(sectionId);
runner.expect(section.isEditing()).toBeTruthy();
// Test stop editing
section.stopEditing();
runner.expect(section.isEditing()).toBeFalsy();
});
runner.it('should preserve event system functionality', () => {
const SectionManager = global.ExtractedSectionManager;
const manager = new SectionManager();
let eventFired = false;
let eventData = null;
manager.on('test-event', (data) => {
eventFired = true;
eventData = data;
});
manager.emit('test-event', { test: 'data' });
runner.expect(eventFired).toBeTruthy();
runner.expect(eventData).toEqual({ test: 'data' });
});
runner.it('should preserve document status functionality', () => {
const SectionManager = global.ExtractedSectionManager;
const manager = new SectionManager();
manager.createSectionsFromMarkdown('# Test\nContent');
const status = manager.getDocumentStatus();
runner.expect(status).toHaveProperty('totalSections');
runner.expect(status).toHaveProperty('editingSections');
runner.expect(status.totalSections).toBe(1);
});
runner.it('should preserve getAllSections functionality', () => {
const SectionManager = global.ExtractedSectionManager;
const manager = new SectionManager();
const testMarkdown = '# One\nContent\n\n# Two\nMore content';
manager.createSectionsFromMarkdown(testMarkdown);
const allSections = manager.getAllSections();
runner.expect(Array.isArray(allSections)).toBeTruthy();
runner.expect(allSections.length).toBe(2);
});
runner.it('should preserve section splitting functionality', () => {
const SectionManager = global.ExtractedSectionManager;
const manager = new SectionManager();
const sections = manager.createSectionsFromMarkdown('# Original\nContent');
const sectionId = sections[0].id;
const newContent = '# Split 1\nContent 1\n\n# Split 2\nContent 2';
const newSections = manager.handleSectionSplit(sectionId, newContent);
runner.expect(Array.isArray(newSections)).toBeTruthy();
runner.expect(newSections.length).toBe(2);
runner.expect(manager.sections.has(sectionId)).toBeFalsy(); // Original removed
});
runner.it('should preserve Section class functionality', () => {
const Section = global.ExtractedSection;
const EditState = global.ExtractedEditState;
const section = new Section('test-id', '# Test Content', 'heading');
runner.expect(section.id).toBe('test-id');
runner.expect(section.currentMarkdown).toBe('# Test Content');
runner.expect(section.type).toBe('heading');
runner.expect(section.state).toBe(EditState.ORIGINAL);
});
runner.it('should preserve Section ID generation', () => {
const Section = global.ExtractedSection;
const id1 = Section.generateId('# Test Heading', 0);
const id2 = Section.generateId('# Different Heading', 1);
runner.expect(typeof id1 === 'string').toBeTruthy();
runner.expect(typeof id2 === 'string').toBeTruthy();
runner.expect(id1).toContain('section-');
runner.expect(id2).toContain('section-');
runner.expect(id1 !== id2).toBeTruthy(); // Should be unique
});
runner.it('should preserve Section type detection', () => {
const Section = global.ExtractedSection;
const SectionType = global.ExtractedSectionType;
runner.expect(Section.detectType('# Heading')).toBe(SectionType.HEADING);
runner.expect(Section.detectType('![Image](url)')).toBe(SectionType.IMAGE);
runner.expect(Section.detectType('```code```')).toBe(SectionType.CODE);
runner.expect(Section.detectType('Regular paragraph')).toBe(SectionType.PARAGRAPH);
});
// Comparative test - verify extracted component behaves identically to original
runner.it('should behave identically to original monolithic component', () => {
// Load both components
const originalModule = require('/home/worsch/markitect_project/markitect/static/editor.js');
const extractedModule = require('../core/section-manager.js');
const originalManager = new originalModule.SectionManager();
const extractedManager = new extractedModule.SectionManager();
const testMarkdown = '# Test\nContent\n\n## Subheading\nMore content';
// Debug: Check what each component produces
console.log('Creating sections with original component...');
const originalSections = originalManager.createSectionsFromMarkdown(testMarkdown);
console.log(`Original produced ${originalSections.length} sections`);
console.log('Creating sections with extracted component...');
const extractedSections = extractedManager.createSectionsFromMarkdown(testMarkdown);
console.log(`Extracted produced ${extractedSections.length} sections`);
if (originalSections.length > 0) {
console.log('Original first section:', originalSections[0].currentMarkdown);
}
if (extractedSections.length > 0) {
console.log('Extracted first section:', extractedSections[0].currentMarkdown);
}
// Should have same number of sections
runner.expect(extractedSections.length).toBe(originalSections.length);
// Should have same content
for (let i = 0; i < originalSections.length; i++) {
runner.expect(extractedSections[i].currentMarkdown).toBe(originalSections[i].currentMarkdown);
runner.expect(extractedSections[i].type).toBe(originalSections[i].type);
}
// Should have same document status structure
const originalStatus = originalManager.getDocumentStatus();
const extractedStatus = extractedManager.getDocumentStatus();
console.log('Original status:', originalStatus);
console.log('Extracted status:', extractedStatus);
runner.expect(extractedStatus.totalSections).toBe(originalStatus.totalSections);
runner.expect(extractedStatus.editingSections).toBe(originalStatus.editingSections);
});
});
module.exports = runner;
// Run tests if called directly
if (require.main === module) {
console.log('🧪 Testing Extracted SectionManager Component');
runner.run().then(() => {
console.log('✅ Extracted SectionManager tests completed');
});
}

View File

@@ -1,305 +0,0 @@
#!/usr/bin/env node
/**
* Full Integration Test
*
* Tests that all extracted components (SectionManager, DOMRenderer,
* DebugPanel, DocumentControls) work together as a complete system.
*/
const RefactorTestRunner = require('./refactor-test-runner.js');
const runner = new RefactorTestRunner();
runner.describe('Full Component Integration Tests', () => {
runner.it('should load all extracted components', () => {
try {
// Load all extracted components
const sectionModule = require('../core/section-manager.js');
const domModule = require('../components/dom-renderer.js');
const debugModule = require('../components/debug-panel.js');
const controlsModule = require('../components/document-controls.js');
runner.expect(sectionModule.SectionManager).toBeTruthy();
runner.expect(domModule.DOMRenderer).toBeTruthy();
runner.expect(debugModule.DebugPanel).toBeTruthy();
runner.expect(controlsModule.DocumentControls).toBeTruthy();
// Set globals for other tests
global.ExtractedSectionManager = sectionModule.SectionManager;
global.ExtractedDOMRenderer = domModule.DOMRenderer;
global.ExtractedDebugPanel = debugModule.DebugPanel;
global.ExtractedDocumentControls = controlsModule.DocumentControls;
} catch (error) {
throw new Error(`Failed to load extracted components: ${error.message}`);
}
});
runner.it('should support complete document editing workflow with all components', () => {
const SectionManager = global.ExtractedSectionManager;
const DOMRenderer = global.ExtractedDOMRenderer;
const DebugPanel = global.ExtractedDebugPanel;
const DocumentControls = global.ExtractedDocumentControls;
// Setup DOM container
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
document.body.appendChild(container);
// Create all components
const sectionManager = new SectionManager();
const domRenderer = new DOMRenderer(sectionManager, container);
const debugPanel = new DebugPanel();
const documentControls = new DocumentControls();
// Setup document controls
documentControls.create();
// Wire up event handlers for debugging
sectionManager.on('sections-created', (data) => {
debugPanel.addMessage(`Created ${data.count} sections`, 'INFO');
});
sectionManager.on('edit-started', (data) => {
debugPanel.addMessage(`Edit started for section: ${data.sectionId}`, 'DEBUG');
});
// Test workflow: Create document
const testMarkdown = `# Document Title
Introduction paragraph with some content.
## Section A
Content for section A with details.
![Test Image](https://example.com/test.jpg)
### Subsection A.1
More detailed content here.`;
// Create sections
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
runner.expect(sections.length).toBe(4);
// Render sections
domRenderer.renderAllSections(sections);
const renderedElements = container.querySelectorAll('.ui-edit-section');
runner.expect(renderedElements.length).toBe(sections.length);
// Test editing workflow
const firstSection = sections[0];
sectionManager.startEditing(firstSection.id);
runner.expect(firstSection.isEditing()).toBeTruthy();
// Check debug messages were created
runner.expect(debugPanel.getMessageCount()).toBe(2); // sections-created + edit-started
// Test document controls functionality
const controlPanel = documentControls.getControlPanel();
runner.expect(controlPanel).toBeTruthy();
runner.expect(document.getElementById('save-document')).toBeTruthy();
runner.expect(document.getElementById('toggle-debug')).toBeTruthy();
// Cleanup
document.body.removeChild(container);
documentControls.destroy();
});
runner.it('should support debug panel integration with document controls', () => {
const DebugPanel = global.ExtractedDebugPanel;
const DocumentControls = global.ExtractedDocumentControls;
// Create components
const debugPanel = new DebugPanel();
const documentControls = new DocumentControls();
// Setup document controls
documentControls.create();
// Setup debug panel toggle handler
const handlers = {
'toggle-debug': () => debugPanel.toggle()
};
documentControls.setEventHandlers(handlers);
// Test debug toggle functionality
const debugButton = documentControls.getButton('toggle-debug');
runner.expect(debugButton).toBeTruthy();
// Add some debug messages
debugPanel.addMessage('Test message 1', 'INFO');
debugPanel.addMessage('Test message 2', 'ERROR');
// Simulate button click to show debug panel
debugButton.click();
runner.expect(debugPanel.isActive).toBeTruthy();
// Simulate button click to hide debug panel
debugButton.click();
runner.expect(debugPanel.isActive).toBeFalsy();
// Cleanup
documentControls.destroy();
});
runner.it('should support event-driven communication between all components', () => {
const SectionManager = global.ExtractedSectionManager;
const DOMRenderer = global.ExtractedDOMRenderer;
const DebugPanel = global.ExtractedDebugPanel;
const DocumentControls = global.ExtractedDocumentControls;
// Setup container
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
document.body.appendChild(container);
// Create components
const sectionManager = new SectionManager();
const domRenderer = new DOMRenderer(sectionManager, container);
const debugPanel = new DebugPanel();
const documentControls = new DocumentControls();
documentControls.create();
// Setup comprehensive event handling
let eventLog = [];
sectionManager.on('sections-created', (data) => {
eventLog.push(`sections-created: ${data.count} sections`);
debugPanel.addMessage(`Sections created: ${data.count}`, 'INFO');
});
sectionManager.on('edit-started', (data) => {
eventLog.push(`edit-started: ${data.sectionId}`);
debugPanel.addMessage(`Edit started: ${data.sectionId}`, 'DEBUG');
});
sectionManager.on('changes-accepted', (data) => {
eventLog.push(`changes-accepted: ${data.sectionId}`);
debugPanel.addMessage(`Changes accepted: ${data.sectionId}`, 'SUCCESS');
});
// Test complete workflow
const testMarkdown = '# Test\nContent for testing';
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
domRenderer.renderAllSections(sections);
// Start editing
sectionManager.startEditing(sections[0].id);
sectionManager.updateContent(sections[0].id, '# Updated Test\nUpdated content');
sectionManager.acceptChanges(sections[0].id);
// Verify events were logged
runner.expect(eventLog.length).toBe(3);
runner.expect(eventLog[0]).toContain('sections-created');
runner.expect(eventLog[1]).toContain('edit-started');
runner.expect(eventLog[2]).toContain('changes-accepted');
// Verify debug messages were created
runner.expect(debugPanel.getMessageCount()).toBe(3);
// Test document controls status update
const status = sectionManager.getDocumentStatus();
documentControls.updateStatus(status);
runner.expect(documentControls.lastStatus).toBeTruthy();
// Cleanup
document.body.removeChild(container);
documentControls.destroy();
});
runner.it('should handle error scenarios gracefully across components', () => {
const SectionManager = global.ExtractedSectionManager;
const DOMRenderer = global.ExtractedDOMRenderer;
const DebugPanel = global.ExtractedDebugPanel;
const DocumentControls = global.ExtractedDocumentControls;
// Test component creation without proper DOM setup
const debugPanel = new DebugPanel();
const documentControls = new DocumentControls();
// These should not throw errors
try {
debugPanel.toggle(); // No DOM elements
debugPanel.update(); // No DOM elements
documentControls.show(); // No control panel created yet
documentControls.hide(); // No control panel created yet
runner.expect(true).toBeTruthy(); // If we get here, no errors were thrown
} catch (error) {
throw new Error(`Components should handle missing DOM gracefully: ${error.message}`);
}
// Test section manager with invalid input
const sectionManager = new SectionManager();
const sections = sectionManager.createSectionsFromMarkdown('');
runner.expect(sections.length).toBe(0);
// Test DOM renderer with invalid container
try {
const invalidRenderer = new DOMRenderer(sectionManager, null);
runner.expect(invalidRenderer.container).toBeFalsy();
} catch (error) {
// This is acceptable - constructor might validate input
runner.expect(typeof error.message === 'string').toBeTruthy();
}
});
runner.it('should support scalable architecture with component lifecycle', () => {
const SectionManager = global.ExtractedSectionManager;
const DOMRenderer = global.ExtractedDOMRenderer;
const DebugPanel = global.ExtractedDebugPanel;
const DocumentControls = global.ExtractedDocumentControls;
// Test multiple instances
const sectionManager1 = new SectionManager();
const sectionManager2 = new SectionManager();
const debugPanel1 = new DebugPanel();
const debugPanel2 = new DebugPanel();
// Each should be independent
debugPanel1.addMessage('Message from panel 1', 'INFO');
debugPanel2.addMessage('Message from panel 2', 'ERROR');
runner.expect(debugPanel1.getMessageCount()).toBe(1);
runner.expect(debugPanel2.getMessageCount()).toBe(1);
// Test section managers are independent
const sections1 = sectionManager1.createSectionsFromMarkdown('# Document 1');
const sections2 = sectionManager2.createSectionsFromMarkdown('# Document 2');
runner.expect(sections1.length).toBe(1);
runner.expect(sections2.length).toBe(1);
runner.expect(sections1[0]).toBeTruthy();
runner.expect(sections2[0]).toBeTruthy();
// IDs should be different (each section gets unique ID)
const id1 = sections1[0].id;
const id2 = sections2[0].id;
runner.expect(id1 !== id2).toBeTruthy();
// Test document controls lifecycle
const controls1 = new DocumentControls();
const controls2 = new DocumentControls();
controls1.create();
runner.expect(document.getElementById('markitect-global-controls')).toBeTruthy();
controls2.create(); // Should replace the first one
runner.expect(document.getElementById('markitect-global-controls')).toBeTruthy();
controls2.destroy();
runner.expect(document.getElementById('markitect-global-controls')).toBeFalsy();
});
});
module.exports = runner;
// Run tests if called directly
if (require.main === module) {
console.log('🧪 Running Full Component Integration Tests');
runner.run().then(() => {
console.log('✅ Full integration tests completed');
});
}

View File

@@ -1,285 +0,0 @@
#!/usr/bin/env node
/**
* Real User Functionality Tests
*
* This test file validates the actual functionality that users experience,
* not just internal API calls. It tests the complete user workflow.
*/
const RefactorTestRunner = require('./refactor-test-runner.js');
const runner = new RefactorTestRunner();
runner.describe('Real User Functionality Tests', () => {
runner.it('should allow users to edit content and see changes in DOM', () => {
// Load all extracted components
const sectionModule = require('../core/section-manager.js');
const domModule = require('../components/dom-renderer.js');
const debugModule = require('../components/debug-panel.js');
const controlsModule = require('../components/document-controls.js');
const { SectionManager } = sectionModule;
const { DOMRenderer } = domModule;
const { DebugPanel } = debugModule;
const { DocumentControls } = controlsModule;
// Setup DOM container
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
document.body.appendChild(container);
// Create components
const sectionManager = new SectionManager();
const domRenderer = new DOMRenderer(sectionManager, container);
const debugPanel = new DebugPanel();
const documentControls = new DocumentControls();
// Setup document controls
documentControls.create();
// Create sections from test markdown
const testMarkdown = `# Original Title\nOriginal content that should be editable.`;
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
domRenderer.renderAllSections(sections);
const firstSection = sections[0];
const sectionElement = container.querySelector(`[data-section-id="${firstSection.id}"]`);
// Verify original content is rendered
runner.expect(sectionElement.innerHTML).toContain('Original Title');
// Simulate user clicking on section
const clickEvent = new Event('click', { bubbles: true });
sectionElement.dispatchEvent(clickEvent);
// Verify editing state is active
runner.expect(firstSection.isEditing()).toBeTruthy();
// Find the floating menu and edit controls
const floatingMenu = document.querySelector('.ui-edit-floating-menu');
runner.expect(floatingMenu).toBeTruthy();
const textarea = floatingMenu.querySelector('textarea');
const acceptButton = Array.from(floatingMenu.querySelectorAll('button')).find(btn => btn.textContent.includes('Accept'));
runner.expect(textarea).toBeTruthy();
runner.expect(acceptButton).toBeTruthy();
// Simulate user editing content
const newContent = '# Updated Title\nCompletely new content added by user.';
textarea.value = newContent;
// Simulate user clicking accept
acceptButton.click();
// Verify section is no longer editing
runner.expect(firstSection.isEditing()).toBeFalsy();
// Verify floating menu is gone
const menuAfterAccept = document.querySelector('.ui-edit-floating-menu');
runner.expect(menuAfterAccept).toBeFalsy();
// CRITICAL TEST: Verify DOM was actually updated with new content
const updatedElement = container.querySelector(`[data-section-id="${firstSection.id}"]`);
runner.expect(updatedElement.innerHTML).toContain('Updated Title');
runner.expect(updatedElement.innerHTML).toContain('Completely new content');
runner.expect(updatedElement.innerHTML).not.toContain('Original Title');
// Cleanup
document.body.removeChild(container);
documentControls.destroy();
});
runner.it('should allow users to reset all changes', () => {
// Setup similar to above
const sectionModule = require('../core/section-manager.js');
const domModule = require('../components/dom-renderer.js');
const controlsModule = require('../components/document-controls.js');
const { SectionManager } = sectionModule;
const { DOMRenderer } = domModule;
const { DocumentControls } = controlsModule;
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
document.body.appendChild(container);
const sectionManager = new SectionManager();
const domRenderer = new DOMRenderer(sectionManager, container);
const documentControls = new DocumentControls();
documentControls.create();
// Create and modify content
const testMarkdown = `# Test Section\nOriginal content for reset test.`;
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
domRenderer.renderAllSections(sections);
const firstSection = sections[0];
// Make changes to the section
sectionManager.startEditing(firstSection.id);
sectionManager.updateContent(firstSection.id, '# Modified Title\nModified content.');
sectionManager.acceptChanges(firstSection.id);
// Verify changes are applied
let sectionElement = container.querySelector(`[data-section-id="${firstSection.id}"]`);
runner.expect(sectionElement.innerHTML).toContain('Modified Title');
runner.expect(firstSection.hasChanges()).toBeTruthy();
// Test reset functionality
const resetButton = documentControls.getButton('reset-all');
runner.expect(resetButton).toBeTruthy();
// Click reset button
resetButton.click();
// Verify content is reset
sectionElement = container.querySelector(`[data-section-id="${firstSection.id}"]`);
runner.expect(sectionElement.innerHTML).toContain('Test Section');
runner.expect(sectionElement.innerHTML).not.toContain('Modified Title');
runner.expect(firstSection.hasChanges()).toBeFalsy();
// Cleanup
document.body.removeChild(container);
documentControls.destroy();
});
runner.it('should handle cancel operations correctly', () => {
const sectionModule = require('../core/section-manager.js');
const domModule = require('../components/dom-renderer.js');
const { SectionManager } = sectionModule;
const { DOMRenderer } = domModule;
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
document.body.appendChild(container);
const sectionManager = new SectionManager();
const domRenderer = new DOMRenderer(sectionManager, container);
const testMarkdown = `# Cancel Test\nContent that should remain unchanged.`;
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
domRenderer.renderAllSections(sections);
const firstSection = sections[0];
const originalContent = firstSection.currentMarkdown;
// Start editing
const sectionElement = container.querySelector(`[data-section-id="${firstSection.id}"]`);
sectionElement.click();
// Make changes but cancel them
const floatingMenu = document.querySelector('.ui-edit-floating-menu');
const textarea = floatingMenu.querySelector('textarea');
const cancelButton = Array.from(floatingMenu.querySelectorAll('button')).find(btn => btn.textContent.includes('Cancel'));
textarea.value = '# This should be cancelled\nThis content should not appear.';
cancelButton.click();
// Verify content is unchanged
const unchangedElement = container.querySelector(`[data-section-id="${firstSection.id}"]`);
runner.expect(unchangedElement.innerHTML).toContain('Cancel Test');
runner.expect(unchangedElement.innerHTML).not.toContain('This should be cancelled');
runner.expect(firstSection.currentMarkdown).toBe(originalContent);
// Cleanup
document.body.removeChild(container);
});
runner.it('should validate the complete editing workflow', () => {
// This test validates the entire user experience end-to-end
const sectionModule = require('../core/section-manager.js');
const domModule = require('../components/dom-renderer.js');
const debugModule = require('../components/debug-panel.js');
const controlsModule = require('../components/document-controls.js');
const { SectionManager } = sectionModule;
const { DOMRenderer } = domModule;
const { DebugPanel } = debugModule;
const { DocumentControls } = controlsModule;
const container = document.createElement('div');
container.innerHTML = '<div id="markdown-content"></div>';
document.body.appendChild(container);
const sectionManager = new SectionManager();
const domRenderer = new DOMRenderer(sectionManager, container);
const debugPanel = new DebugPanel();
const documentControls = new DocumentControls();
documentControls.create();
// Multi-section document
const testMarkdown = `# Document Title
Introduction paragraph.
## Section A
Content for section A.
## Section B
Content for section B.`;
const sections = sectionManager.createSectionsFromMarkdown(testMarkdown);
domRenderer.renderAllSections(sections);
// Verify all sections are rendered
const renderedSections = container.querySelectorAll('.ui-edit-section');
runner.expect(renderedSections.length).toBe(sections.length);
// Test editing multiple sections
const firstSection = sections[0];
const secondSection = sections[2]; // Section A
// Edit first section
renderedSections[0].click();
let floatingMenu = document.querySelector('.ui-edit-floating-menu');
let textarea = floatingMenu.querySelector('textarea');
let acceptButton = Array.from(floatingMenu.querySelectorAll('button')).find(btn => btn.textContent.includes('Accept'));
textarea.value = '# Updated Document Title\nUpdated introduction.';
acceptButton.click();
// Edit second section
renderedSections[2].click();
floatingMenu = document.querySelector('.ui-edit-floating-menu');
textarea = floatingMenu.querySelector('textarea');
acceptButton = Array.from(floatingMenu.querySelectorAll('button')).find(btn => btn.textContent.includes('Accept'));
textarea.value = '## Updated Section A\nCompletely new content for section A.';
acceptButton.click();
// Verify both sections were updated
const updatedSections = container.querySelectorAll('.ui-edit-section');
runner.expect(updatedSections[0].innerHTML).toContain('Updated Document Title');
runner.expect(updatedSections[2].innerHTML).toContain('Updated Section A');
// Test reset restores all sections
const resetButton = documentControls.getButton('reset-all');
resetButton.click();
const resetSections = container.querySelectorAll('.ui-edit-section');
runner.expect(resetSections[0].innerHTML).toContain('Document Title');
runner.expect(resetSections[0].innerHTML).not.toContain('Updated Document Title');
runner.expect(resetSections[2].innerHTML).toContain('Section A');
runner.expect(resetSections[2].innerHTML).not.toContain('Updated Section A');
// Cleanup
document.body.removeChild(container);
documentControls.destroy();
});
});
module.exports = runner;
// Run tests if called directly
if (require.main === module) {
console.log('🧪 Running Real User Functionality Tests');
runner.run().then(() => {
console.log('✅ Real user functionality tests completed');
console.log('These tests validate what users actually experience, not just internal APIs');
});
}

View File

@@ -1,196 +0,0 @@
#!/usr/bin/env node
/**
* TDD Test for SectionManager Component Extraction
*
* Tests the extraction of SectionManager from the monolithic editor.js
* Ensures all functionality is preserved during refactoring.
*/
const RefactorTestRunner = require('./refactor-test-runner.js');
const runner = new RefactorTestRunner();
// First, let's define what the SectionManager API should look like
const EXPECTED_SECTION_MANAGER_API = [
'constructor',
'createSectionsFromMarkdown',
'startEditing',
'stopEditing',
'getAllSections',
'sections', // Map property, not method
'getDocumentStatus',
'getDocumentMarkdown',
'on', // event system
'emit', // event system
'handleSectionSplit',
'updateContent',
'acceptChanges',
'cancelChanges',
'resetSection'
];
runner.describe('SectionManager Component Extraction', () => {
runner.it('should define expected API methods', () => {
// This test defines what we expect from the extracted SectionManager
const expectedMethods = EXPECTED_SECTION_MANAGER_API;
runner.expect(expectedMethods.length).toBe(15);
runner.expect(expectedMethods).toContain('createSectionsFromMarkdown');
runner.expect(expectedMethods).toContain('startEditing');
runner.expect(expectedMethods).toContain('stopEditing');
});
runner.it('should extract from monolithic editor.js', () => {
// Load the monolithic editor.js to extract SectionManager
delete require.cache[require.resolve('/home/worsch/markitect_project/markitect/static/editor.js')];
try {
const editorModule = require('/home/worsch/markitect_project/markitect/static/editor.js');
runner.expect(editorModule.SectionManager).toBeTruthy();
// Set global for other tests
global.SectionManager = editorModule.SectionManager;
global.Section = editorModule.Section;
global.EditState = editorModule.EditState;
} catch (error) {
throw new Error(`Failed to load monolithic editor.js: ${error.message}`);
}
});
runner.it('should preserve SectionManager constructor functionality', () => {
const SectionManager = global.SectionManager;
const manager = new SectionManager();
runner.expect(manager).toBeInstanceOf(SectionManager);
runner.expect(manager.sections).toBeInstanceOf(Map);
});
runner.it('should preserve createSectionsFromMarkdown functionality', () => {
const SectionManager = global.SectionManager;
const manager = new SectionManager();
const testMarkdown = `# Heading 1\nContent 1\n\n## Heading 2\nContent 2`;
const sections = manager.createSectionsFromMarkdown(testMarkdown);
runner.expect(Array.isArray(sections)).toBeTruthy();
runner.expect(sections.length).toBe(2);
runner.expect(sections[0].currentMarkdown).toContain('Heading 1');
runner.expect(sections[1].currentMarkdown).toContain('Heading 2');
});
runner.it('should preserve section editing state management', () => {
const SectionManager = global.SectionManager;
const manager = new SectionManager();
const sections = manager.createSectionsFromMarkdown('# Test\nContent');
const sectionId = sections[0].id;
// Test start editing
runner.expect(manager.startEditing(sectionId)).toBeTruthy();
const section = manager.sections.get(sectionId);
runner.expect(section.isEditing()).toBeTruthy();
// Test stop editing
section.stopEditing();
runner.expect(section.isEditing()).toBeFalsy();
});
runner.it('should preserve event system functionality', () => {
const SectionManager = global.SectionManager;
const manager = new SectionManager();
let eventFired = false;
let eventData = null;
manager.on('test-event', (data) => {
eventFired = true;
eventData = data;
});
manager.emit('test-event', { test: 'data' });
runner.expect(eventFired).toBeTruthy();
runner.expect(eventData).toEqual({ test: 'data' });
});
runner.it('should preserve document status functionality', () => {
const SectionManager = global.SectionManager;
const manager = new SectionManager();
manager.createSectionsFromMarkdown('# Test\nContent');
const status = manager.getDocumentStatus();
runner.expect(status).toHaveProperty('totalSections');
runner.expect(status).toHaveProperty('editingSections');
runner.expect(status.totalSections).toBe(1);
});
runner.it('should preserve getAllSections functionality', () => {
const SectionManager = global.SectionManager;
const manager = new SectionManager();
const testMarkdown = '# One\nContent\n\n# Two\nMore content';
manager.createSectionsFromMarkdown(testMarkdown);
const allSections = manager.getAllSections();
runner.expect(Array.isArray(allSections)).toBeTruthy();
runner.expect(allSections.length).toBe(2);
});
runner.it('should preserve section splitting functionality', () => {
const SectionManager = global.SectionManager;
const manager = new SectionManager();
const sections = manager.createSectionsFromMarkdown('# Original\nContent');
const sectionId = sections[0].id;
const newContent = '# Split 1\nContent 1\n\n# Split 2\nContent 2';
const newSections = manager.handleSectionSplit(sectionId, newContent);
runner.expect(Array.isArray(newSections)).toBeTruthy();
runner.expect(newSections.length).toBe(2);
runner.expect(manager.sections.has(sectionId)).toBeFalsy(); // Original removed
});
});
// Export API tests for use during extraction
const SECTION_MANAGER_API_TESTS = [
(SectionManager) => {
const manager = new SectionManager();
if (!manager.sections || !(manager.sections instanceof Map)) {
throw new Error('sections property missing or not a Map');
}
},
(SectionManager) => {
const manager = new SectionManager();
if (typeof manager.createSectionsFromMarkdown !== 'function') {
throw new Error('createSectionsFromMarkdown method missing');
}
},
(SectionManager) => {
const manager = new SectionManager();
if (typeof manager.startEditing !== 'function') {
throw new Error('startEditing method missing');
}
},
(SectionManager) => {
const manager = new SectionManager();
if (typeof manager.stopEditing !== 'function') {
throw new Error('stopEditing method missing');
}
}
];
module.exports = {
runner,
EXPECTED_SECTION_MANAGER_API,
SECTION_MANAGER_API_TESTS
};
// Run tests if called directly
if (require.main === module) {
console.log('🧪 Testing SectionManager Component Extraction');
runner.run().then(() => {
console.log('✅ SectionManager extraction tests completed');
});
}

View File

@@ -1 +0,0 @@
../acorn/bin/acorn

View File

@@ -1 +0,0 @@
../baseline-browser-mapping/dist/cli.js

View File

@@ -1 +0,0 @@
../browserslist/cli.js

View File

@@ -1 +0,0 @@
../create-jest/bin/create-jest.js

View File

@@ -1 +0,0 @@
../escodegen/bin/escodegen.js

View File

@@ -1 +0,0 @@
../escodegen/bin/esgenerate.js

View File

@@ -1 +0,0 @@
../eslint/bin/eslint.js

View File

@@ -1 +0,0 @@
../esprima/bin/esparse.js

View File

@@ -1 +0,0 @@
../esprima/bin/esvalidate.js

View File

@@ -1 +0,0 @@
../import-local/fixtures/cli.js

View File

@@ -1 +0,0 @@
../jest/bin/jest.js

View File

@@ -1 +0,0 @@
../js-yaml/bin/js-yaml.js

View File

@@ -1 +0,0 @@
../jsesc/bin/jsesc

View File

@@ -1 +0,0 @@
../json5/lib/cli.js

View File

@@ -1 +0,0 @@
../which/bin/node-which

View File

@@ -1 +0,0 @@
../@babel/parser/bin/babel-parser.js

View File

@@ -1 +0,0 @@
../regjsparser/bin/parser

View File

@@ -1 +0,0 @@
../resolve/bin/resolve

View File

@@ -1 +0,0 @@
../rimraf/bin.js

View File

@@ -1 +0,0 @@
../semver/bin/semver.js

View File

@@ -1 +0,0 @@
../typescript/bin/tsc

View File

@@ -1 +0,0 @@
../typescript/bin/tsserver

View File

@@ -1 +0,0 @@
../update-browserslist-db/cli.js

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2024 asamuzaK (Kazz)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,316 +0,0 @@
# CSS color
[![build](https://github.com/asamuzaK/cssColor/actions/workflows/node.js.yml/badge.svg)](https://github.com/asamuzaK/cssColor/actions/workflows/node.js.yml)
[![CodeQL](https://github.com/asamuzaK/cssColor/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/asamuzaK/cssColor/actions/workflows/github-code-scanning/codeql)
[![npm (scoped)](https://img.shields.io/npm/v/@asamuzakjp/css-color)](https://www.npmjs.com/package/@asamuzakjp/css-color)
Resolve and convert CSS colors.
## Install
```console
npm i @asamuzakjp/css-color
```
## Usage
```javascript
import { convert, resolve, utils } from '@asamuzakjp/css-color';
const resolvedValue = resolve(
'color-mix(in oklab, lch(67.5345 42.5 258.2), color(srgb 0 0.5 0))'
);
// 'oklab(0.620754 -0.0931934 -0.00374881)'
const convertedValue = covert.colorToHex('lab(46.2775% -47.5621 48.5837)');
// '#008000'
const result = utils.isColor('green');
// true
```
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### resolve(color, opt)
resolves CSS color
#### Parameters
- `color` **[string][133]** color value
- system colors are not supported
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.currentColor` **[string][133]?**
- color to use for `currentcolor` keyword
- if omitted, it will be treated as a missing color,
i.e. `rgb(none none none / none)`
- `opt.customProperty` **[object][135]?**
- custom properties
- pair of `--` prefixed property name as a key and it's value,
e.g.
```javascript
const opt = {
customProperty: {
'--some-color': '#008000',
'--some-length': '16px'
}
};
```
- and/or `callback` function to get the value of the custom property,
e.g.
```javascript
const node = document.getElementById('foo');
const opt = {
customProperty: {
callback: node.style.getPropertyValue
}
};
```
- `opt.dimension` **[object][135]?**
- dimension, e.g. for converting relative length to pixels
- pair of unit as a key and number in pixels as it's value,
e.g. suppose `1em === 12px`, `1rem === 16px` and `100vw === 1024px`, then
```javascript
const opt = {
dimension: {
em: 12,
rem: 16,
vw: 10.24
}
};
```
- and/or `callback` function to get the value as a number in pixels,
e.g.
```javascript
const opt = {
dimension: {
callback: unit => {
switch (unit) {
case 'em':
return 12;
case 'rem':
return 16;
case 'vw':
return 10.24;
default:
return;
}
}
}
};
```
- `opt.format` **[string][133]?**
- output format, one of below
- `computedValue` (default), [computed value][139] of the color
- `specifiedValue`, [specified value][140] of the color
- `hex`, hex color notation, i.e. `#rrggbb`
- `hexAlpha`, hex color notation with alpha channel, i.e. `#rrggbbaa`
Returns **[string][133]?** one of `rgba?()`, `#rrggbb(aa)?`, `color-name`, `color(color-space r g b / alpha)`, `color(color-space x y z / alpha)`, `(ok)?lab(l a b / alpha)`, `(ok)?lch(l c h / alpha)`, `'(empty-string)'`, `null`
- in `computedValue`, values are numbers, however `rgb()` values are integers
- in `specifiedValue`, returns `empty string` for unknown and/or invalid color
- in `hex`, returns `null` for `transparent`, and also returns `null` if any of `r`, `g`, `b`, `alpha` is not a number
- in `hexAlpha`, returns `#00000000` for `transparent`, however returns `null` if any of `r`, `g`, `b`, `alpha` is not a number
### convert
Contains various color conversion functions.
### convert.numberToHex(value)
convert number to hex string
#### Parameters
- `value` **[number][134]** color value
Returns **[string][133]** hex string: 00..ff
### convert.colorToHex(value, opt)
convert color to hex
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.alpha` **[boolean][136]?** return in #rrggbbaa notation
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[string][133]** #rrggbb(aa)?
### convert.colorToHsl(value, opt)
convert color to hsl
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[h, s, l, alpha]
### convert.colorToHwb(value, opt)
convert color to hwb
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[h, w, b, alpha]
### convert.colorToLab(value, opt)
convert color to lab
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[l, a, b, alpha]
### convert.colorToLch(value, opt)
convert color to lch
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[l, c, h, alpha]
### convert.colorToOklab(value, opt)
convert color to oklab
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[l, a, b, alpha]
### convert.colorToOklch(value, opt)
convert color to oklch
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[l, c, h, alpha]
### convert.colorToRgb(value, opt)
convert color to rgb
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[r, g, b, alpha]
### convert.colorToXyz(value, opt)
convert color to xyz
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
- `opt.d50` **[boolean][136]?** xyz in d50 white point
Returns **[Array][137]<[number][134]>** \[x, y, z, alpha]
### convert.colorToXyzD50(value, opt)
convert color to xyz-d50
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[x, y, z, alpha]
### utils
Contains utility functions.
### utils.isColor(color)
is valid color type
#### Parameters
- `color` **[string][133]** color value
- system colors are not supported
Returns **[boolean][136]**
## Acknowledgments
The following resources have been of great help in the development of the CSS color.
- [csstools/postcss-plugins](https://github.com/csstools/postcss-plugins)
- [lru-cache](https://github.com/isaacs/node-lru-cache)
---
Copyright (c) 2024 [asamuzaK (Kazz)](https://github.com/asamuzaK/)
[133]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[134]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[135]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[136]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[137]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
[138]: https://w3c.github.io/csswg-drafts/css-color-4/#color-conversion-code
[139]: https://developer.mozilla.org/en-US/docs/Web/CSS/computed_value
[140]: https://developer.mozilla.org/en-US/docs/Web/CSS/specified_value
[141]: https://www.npmjs.com/package/@csstools/css-calc

View File

@@ -1,15 +0,0 @@
The ISC License
Copyright (c) 2010-2023 Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@@ -1,331 +0,0 @@
# lru-cache
A cache object that deletes the least-recently-used items.
Specify a max number of the most recently used items that you
want to keep, and this cache will keep that many of the most
recently accessed items.
This is not primarily a TTL cache, and does not make strong TTL
guarantees. There is no preemptive pruning of expired items by
default, but you _may_ set a TTL on the cache or on a single
`set`. If you do so, it will treat expired items as missing, and
delete them when fetched. If you are more interested in TTL
caching than LRU caching, check out
[@isaacs/ttlcache](http://npm.im/@isaacs/ttlcache).
As of version 7, this is one of the most performant LRU
implementations available in JavaScript, and supports a wide
diversity of use cases. However, note that using some of the
features will necessarily impact performance, by causing the
cache to have to do more work. See the "Performance" section
below.
## Installation
```bash
npm install lru-cache --save
```
## Usage
```js
// hybrid module, either works
import { LRUCache } from 'lru-cache'
// or:
const { LRUCache } = require('lru-cache')
// or in minified form for web browsers:
import { LRUCache } from 'http://unpkg.com/lru-cache@9/dist/mjs/index.min.mjs'
// At least one of 'max', 'ttl', or 'maxSize' is required, to prevent
// unsafe unbounded storage.
//
// In most cases, it's best to specify a max for performance, so all
// the required memory allocation is done up-front.
//
// All the other options are optional, see the sections below for
// documentation on what each one does. Most of them can be
// overridden for specific items in get()/set()
const options = {
max: 500,
// for use with tracking overall storage size
maxSize: 5000,
sizeCalculation: (value, key) => {
return 1
},
// for use when you need to clean up something when objects
// are evicted from the cache
dispose: (value, key) => {
freeFromMemoryOrWhatever(value)
},
// how long to live in ms
ttl: 1000 * 60 * 5,
// return stale items before removing from cache?
allowStale: false,
updateAgeOnGet: false,
updateAgeOnHas: false,
// async method to use for cache.fetch(), for
// stale-while-revalidate type of behavior
fetchMethod: async (
key,
staleValue,
{ options, signal, context }
) => {},
}
const cache = new LRUCache(options)
cache.set('key', 'value')
cache.get('key') // "value"
// non-string keys ARE fully supported
// but note that it must be THE SAME object, not
// just a JSON-equivalent object.
var someObject = { a: 1 }
cache.set(someObject, 'a value')
// Object keys are not toString()-ed
cache.set('[object Object]', 'a different value')
assert.equal(cache.get(someObject), 'a value')
// A similar object with same keys/values won't work,
// because it's a different object identity
assert.equal(cache.get({ a: 1 }), undefined)
cache.clear() // empty the cache
```
If you put more stuff in the cache, then less recently used items
will fall out. That's what an LRU cache is.
For full description of the API and all options, please see [the
LRUCache typedocs](https://isaacs.github.io/node-lru-cache/)
## Storage Bounds Safety
This implementation aims to be as flexible as possible, within
the limits of safe memory consumption and optimal performance.
At initial object creation, storage is allocated for `max` items.
If `max` is set to zero, then some performance is lost, and item
count is unbounded. Either `maxSize` or `ttl` _must_ be set if
`max` is not specified.
If `maxSize` is set, then this creates a safe limit on the
maximum storage consumed, but without the performance benefits of
pre-allocation. When `maxSize` is set, every item _must_ provide
a size, either via the `sizeCalculation` method provided to the
constructor, or via a `size` or `sizeCalculation` option provided
to `cache.set()`. The size of every item _must_ be a positive
integer.
If neither `max` nor `maxSize` are set, then `ttl` tracking must
be enabled. Note that, even when tracking item `ttl`, items are
_not_ preemptively deleted when they become stale, unless
`ttlAutopurge` is enabled. Instead, they are only purged the
next time the key is requested. Thus, if `ttlAutopurge`, `max`,
and `maxSize` are all not set, then the cache will potentially
grow unbounded.
In this case, a warning is printed to standard error. Future
versions may require the use of `ttlAutopurge` if `max` and
`maxSize` are not specified.
If you truly wish to use a cache that is bound _only_ by TTL
expiration, consider using a `Map` object, and calling
`setTimeout` to delete entries when they expire. It will perform
much better than an LRU cache.
Here is an implementation you may use, under the same
[license](./LICENSE) as this package:
```js
// a storage-unbounded ttl cache that is not an lru-cache
const cache = {
data: new Map(),
timers: new Map(),
set: (k, v, ttl) => {
if (cache.timers.has(k)) {
clearTimeout(cache.timers.get(k))
}
cache.timers.set(
k,
setTimeout(() => cache.delete(k), ttl)
)
cache.data.set(k, v)
},
get: k => cache.data.get(k),
has: k => cache.data.has(k),
delete: k => {
if (cache.timers.has(k)) {
clearTimeout(cache.timers.get(k))
}
cache.timers.delete(k)
return cache.data.delete(k)
},
clear: () => {
cache.data.clear()
for (const v of cache.timers.values()) {
clearTimeout(v)
}
cache.timers.clear()
},
}
```
If that isn't to your liking, check out
[@isaacs/ttlcache](http://npm.im/@isaacs/ttlcache).
## Storing Undefined Values
This cache never stores undefined values, as `undefined` is used
internally in a few places to indicate that a key is not in the
cache.
You may call `cache.set(key, undefined)`, but this is just
an alias for `cache.delete(key)`. Note that this has the effect
that `cache.has(key)` will return _false_ after setting it to
undefined.
```js
cache.set(myKey, undefined)
cache.has(myKey) // false!
```
If you need to track `undefined` values, and still note that the
key is in the cache, an easy workaround is to use a sigil object
of your own.
```js
import { LRUCache } from 'lru-cache'
const undefinedValue = Symbol('undefined')
const cache = new LRUCache(...)
const mySet = (key, value) =>
cache.set(key, value === undefined ? undefinedValue : value)
const myGet = (key, value) => {
const v = cache.get(key)
return v === undefinedValue ? undefined : v
}
```
## Performance
As of January 2022, version 7 of this library is one of the most
performant LRU cache implementations in JavaScript.
Benchmarks can be extremely difficult to get right. In
particular, the performance of set/get/delete operations on
objects will vary _wildly_ depending on the type of key used. V8
is highly optimized for objects with keys that are short strings,
especially integer numeric strings. Thus any benchmark which
tests _solely_ using numbers as keys will tend to find that an
object-based approach performs the best.
Note that coercing _anything_ to strings to use as object keys is
unsafe, unless you can be 100% certain that no other type of
value will be used. For example:
```js
const myCache = {}
const set = (k, v) => (myCache[k] = v)
const get = k => myCache[k]
set({}, 'please hang onto this for me')
set('[object Object]', 'oopsie')
```
Also beware of "Just So" stories regarding performance. Garbage
collection of large (especially: deep) object graphs can be
incredibly costly, with several "tipping points" where it
increases exponentially. As a result, putting that off until
later can make it much worse, and less predictable. If a library
performs well, but only in a scenario where the object graph is
kept shallow, then that won't help you if you are using large
objects as keys.
In general, when attempting to use a library to improve
performance (such as a cache like this one), it's best to choose
an option that will perform well in the sorts of scenarios where
you'll actually use it.
This library is optimized for repeated gets and minimizing
eviction time, since that is the expected need of a LRU. Set
operations are somewhat slower on average than a few other
options, in part because of that optimization. It is assumed
that you'll be caching some costly operation, ideally as rarely
as possible, so optimizing set over get would be unwise.
If performance matters to you:
1. If it's at all possible to use small integer values as keys,
and you can guarantee that no other types of values will be
used as keys, then do that, and use a cache such as
[lru-fast](https://npmjs.com/package/lru-fast), or
[mnemonist's
LRUCache](https://yomguithereal.github.io/mnemonist/lru-cache)
which uses an Object as its data store.
2. Failing that, if at all possible, use short non-numeric
strings (ie, less than 256 characters) as your keys, and use
[mnemonist's
LRUCache](https://yomguithereal.github.io/mnemonist/lru-cache).
3. If the types of your keys will be anything else, especially
long strings, strings that look like floats, objects, or some
mix of types, or if you aren't sure, then this library will
work well for you.
If you do not need the features that this library provides
(like asynchronous fetching, a variety of TTL staleness
options, and so on), then [mnemonist's
LRUMap](https://yomguithereal.github.io/mnemonist/lru-map) is
a very good option, and just slightly faster than this module
(since it does considerably less).
4. Do not use a `dispose` function, size tracking, or especially
ttl behavior, unless absolutely needed. These features are
convenient, and necessary in some use cases, and every attempt
has been made to make the performance impact minimal, but it
isn't nothing.
## Breaking Changes in Version 7
This library changed to a different algorithm and internal data
structure in version 7, yielding significantly better
performance, albeit with some subtle changes as a result.
If you were relying on the internals of LRUCache in version 6 or
before, it probably will not work in version 7 and above.
## Breaking Changes in Version 8
- The `fetchContext` option was renamed to `context`, and may no
longer be set on the cache instance itself.
- Rewritten in TypeScript, so pretty much all the types moved
around a lot.
- The AbortController/AbortSignal polyfill was removed. For this
reason, **Node version 16.14.0 or higher is now required**.
- Internal properties were moved to actual private class
properties.
- Keys and values must not be `null` or `undefined`.
- Minified export available at `'lru-cache/min'`, for both CJS
and MJS builds.
## Breaking Changes in Version 9
- Named export only, no default export.
- AbortController polyfill returned, albeit with a warning when
used.
## Breaking Changes in Version 10
- `cache.fetch()` return type is now `Promise<V | undefined>`
instead of `Promise<V | void>`. This is an irrelevant change
practically speaking, but can require changes for TypeScript
users.
For more info, see the [change log](CHANGELOG.md).

View File

@@ -1,116 +0,0 @@
{
"name": "lru-cache",
"publishConfig": {
"tag": "legacy-v10"
},
"description": "A cache object that deletes the least-recently-used items.",
"version": "10.4.3",
"author": "Isaac Z. Schlueter <i@izs.me>",
"keywords": [
"mru",
"lru",
"cache"
],
"sideEffects": false,
"scripts": {
"build": "npm run prepare",
"prepare": "tshy && bash fixup.sh",
"pretest": "npm run prepare",
"presnap": "npm run prepare",
"test": "tap",
"snap": "tap",
"preversion": "npm test",
"postversion": "npm publish",
"prepublishOnly": "git push origin --follow-tags",
"format": "prettier --write .",
"typedoc": "typedoc --tsconfig ./.tshy/esm.json ./src/*.ts",
"benchmark-results-typedoc": "bash scripts/benchmark-results-typedoc.sh",
"prebenchmark": "npm run prepare",
"benchmark": "make -C benchmark",
"preprofile": "npm run prepare",
"profile": "make -C benchmark profile"
},
"main": "./dist/commonjs/index.js",
"types": "./dist/commonjs/index.d.ts",
"tshy": {
"exports": {
".": "./src/index.ts",
"./min": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.min.js"
},
"require": {
"types": "./dist/commonjs/index.d.ts",
"default": "./dist/commonjs/index.min.js"
}
}
}
},
"repository": {
"type": "git",
"url": "git://github.com/isaacs/node-lru-cache.git"
},
"devDependencies": {
"@types/node": "^20.2.5",
"@types/tap": "^15.0.6",
"benchmark": "^2.1.4",
"esbuild": "^0.17.11",
"eslint-config-prettier": "^8.5.0",
"marked": "^4.2.12",
"mkdirp": "^2.1.5",
"prettier": "^2.6.2",
"tap": "^20.0.3",
"tshy": "^2.0.0",
"tslib": "^2.4.0",
"typedoc": "^0.25.3",
"typescript": "^5.2.2"
},
"license": "ISC",
"files": [
"dist"
],
"prettier": {
"semi": false,
"printWidth": 70,
"tabWidth": 2,
"useTabs": false,
"singleQuote": true,
"jsxSingleQuote": false,
"bracketSameLine": true,
"arrowParens": "avoid",
"endOfLine": "lf"
},
"tap": {
"node-arg": [
"--expose-gc"
],
"plugin": [
"@tapjs/clock"
]
},
"exports": {
".": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.js"
},
"require": {
"types": "./dist/commonjs/index.d.ts",
"default": "./dist/commonjs/index.js"
}
},
"./min": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.min.js"
},
"require": {
"types": "./dist/commonjs/index.d.ts",
"default": "./dist/commonjs/index.min.js"
}
}
},
"type": "module",
"module": "./dist/esm/index.js"
}

View File

@@ -1,81 +0,0 @@
{
"name": "@asamuzakjp/css-color",
"description": "CSS color - Resolve and convert CSS colors.",
"author": "asamuzaK",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/asamuzaK/cssColor.git"
},
"homepage": "https://github.com/asamuzaK/cssColor#readme",
"bugs": {
"url": "https://github.com/asamuzaK/cssColor/issues"
},
"files": [
"dist",
"src"
],
"type": "module",
"types": "dist/esm/index.d.ts",
"module": "dist/esm/index.js",
"main": "dist/cjs/index.cjs",
"exports": {
".": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.js"
},
"require": {
"types": "./dist/cjs/index.d.cts",
"default": "./dist/cjs/index.cjs"
}
},
"./package.json": "./package.json"
},
"dependencies": {
"@csstools/css-calc": "^2.1.3",
"@csstools/css-color-parser": "^3.0.9",
"@csstools/css-parser-algorithms": "^3.0.4",
"@csstools/css-tokenizer": "^3.0.3",
"lru-cache": "^10.4.3"
},
"devDependencies": {
"@tanstack/vite-config": "^0.2.0",
"@vitest/coverage-istanbul": "^3.1.4",
"esbuild": "^0.25.4",
"eslint": "^9.27.0",
"eslint-plugin-regexp": "^2.7.0",
"globals": "^16.1.0",
"knip": "^5.56.0",
"neostandard": "^0.12.1",
"prettier": "^3.5.3",
"publint": "^0.3.12",
"rimraf": "^6.0.1",
"tsup": "^8.5.0",
"typescript": "^5.8.3",
"vite": "^6.3.5",
"vitest": "^3.1.4"
},
"packageManager": "pnpm@10.11.0",
"pnpm": {
"onlyBuiltDependencies": [
"esbuild",
"unrs-resolver"
]
},
"scripts": {
"build": "pnpm run clean && pnpm run test && pnpm run knip && pnpm run build:prod && pnpm run build:cjs && pnpm run build:browser && pnpm run publint",
"build:browser": "vite build -c ./vite.browser.config.ts",
"build:prod": "vite build",
"build:cjs": "tsup ./src/index.ts --format=cjs --platform=node --outDir=./dist/cjs/ --sourcemap --dts",
"clean": "rimraf ./coverage ./dist",
"knip": "knip",
"prettier": "prettier . --ignore-unknown --write",
"publint": "publint --strict",
"test": "pnpm run prettier && pnpm run --stream \"/^test:.*/\"",
"test:eslint": "eslint ./src ./test --fix",
"test:types": "tsc",
"test:unit": "vitest"
},
"version": "3.2.0"
}

View File

@@ -1,27 +0,0 @@
/*!
* CSS color - Resolve, parse, convert CSS color.
* @license MIT
* @copyright asamuzaK (Kazz)
* @see {@link https://github.com/asamuzaK/cssColor/blob/main/LICENSE}
*/
import { cssCalc as csscalc } from './js/css-calc';
import { isGradient } from './js/css-gradient';
import { cssVar } from './js/css-var';
import { extractDashedIdent, isColor as iscolor, splitValue } from './js/util';
export { convert } from './js/convert';
export { resolve } from './js/resolve';
/* utils */
export const utils = {
cssCalc: csscalc,
cssVar,
extractDashedIdent,
isColor: iscolor,
isGradient,
splitValue
};
/* TODO: remove later */
/* alias */
export const isColor = utils.isColor;
export const cssCalc = utils.cssCalc;

View File

@@ -1,114 +0,0 @@
/**
* cache
*/
import { LRUCache } from 'lru-cache';
import { Options } from './typedef';
import { valueToJsonString } from './util';
/* numeric constants */
const MAX_CACHE = 4096;
/**
* CacheItem
*/
export class CacheItem {
/* private */
#isNull: boolean;
#item: unknown;
/**
* constructor
*/
constructor(item: unknown, isNull: boolean = false) {
this.#item = item;
this.#isNull = !!isNull;
}
get item() {
return this.#item;
}
get isNull() {
return this.#isNull;
}
}
/**
* NullObject
*/
export class NullObject extends CacheItem {
/**
* constructor
*/
constructor() {
super(Symbol('null'), true);
}
}
/*
* lru cache
*/
export const lruCache = new LRUCache({
max: MAX_CACHE
});
/**
* set cache
* @param key - cache key
* @param value - value to cache
* @returns void
*/
export const setCache = (key: string, value: unknown): void => {
if (key) {
if (value === null) {
lruCache.set(key, new NullObject());
} else if (value instanceof CacheItem) {
lruCache.set(key, value);
} else {
lruCache.set(key, new CacheItem(value));
}
}
};
/**
* get cache
* @param key - cache key
* @returns cached item or false otherwise
*/
export const getCache = (key: string): CacheItem | boolean => {
if (key && lruCache.has(key)) {
const item = lruCache.get(key);
if (item instanceof CacheItem) {
return item;
}
// delete unexpected cached item
lruCache.delete(key);
return false;
}
return false;
};
/**
* create cache key
* @param keyData - key data
* @param [opt] - options
* @returns cache key
*/
export const createCacheKey = (
keyData: Record<string, string>,
opt: Options = {}
): string => {
const { customProperty = {}, dimension = {} } = opt;
let cacheKey = '';
if (
keyData &&
Object.keys(keyData).length &&
typeof customProperty.callback !== 'function' &&
typeof dimension.callback !== 'function'
) {
keyData.opt = valueToJsonString(opt);
cacheKey = valueToJsonString(keyData);
}
return cacheKey;
};

Some files were not shown because too many files have changed in this diff Show More