Compare commits
38
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ef6868f00 | ||
|
|
819c360a6c | ||
|
|
1e848019d9 | ||
|
|
be00bc6f65 | ||
|
|
32334c6631 | ||
|
|
09da0d90aa | ||
|
|
865483b4c6 | ||
|
|
f796a27277 | ||
|
|
321897fd55 | ||
|
|
4da92ab481 | ||
|
|
7831abeed5 | ||
|
|
fe8364e7eb | ||
|
|
3ade397008 | ||
|
|
29084050a7 | ||
|
|
cb611a71df | ||
|
|
fb77b857d2 | ||
|
|
a9a5768144 | ||
|
|
d041212d17 | ||
|
|
b96286ea37 | ||
|
|
dd42d0ab01 | ||
|
|
023e3a8f09 | ||
|
|
52f6e176cd | ||
|
|
b853a1eca3 | ||
|
|
e207b04114 | ||
|
|
c81e285573 | ||
|
|
c6d296a7e2 | ||
|
|
80cf197be6 | ||
|
|
865cd55e21 | ||
|
|
6d2b9619e3 | ||
|
|
65a1fef17c | ||
|
|
ef1a4478c5 | ||
|
|
632c8feb36 | ||
|
|
aa40345281 | ||
|
|
8547b85825 | ||
|
|
4ecad78433 | ||
|
|
ff615d0ee0 | ||
|
|
eb5161b207 | ||
|
|
aed40d7464 |
@@ -15,9 +15,49 @@
|
||||
|
||||
# Exact source/recovery oracles and archived ledgers retain their measured bytes.
|
||||
# Keep ordinary text diffs; only disable checkout/check-in newline conversion.
|
||||
PROJECT_GOAL.md -text whitespace=cr-at-eol
|
||||
harness/fixtures/release1-native/** -text
|
||||
harness/fixtures/interface-operation-probe/** -text
|
||||
harness/history/2026-09-06-ledgers-before-consolidation/** -text
|
||||
harness/history/2026-09-10-ledgers-before-continuation-refresh/** -text
|
||||
harness/history/2026-09-10-ledgers-before-second-consolidation/** -text whitespace=blank-at-eol,blank-at-eof,space-before-tab,cr-at-eol
|
||||
harness/experiments/textual-authoring-20260911-local/** -text whitespace=blank-at-eol,blank-at-eof,space-before-tab,cr-at-eol
|
||||
harness/history/2026-09-11-ytr83r-integration/** -text whitespace=cr-at-eol
|
||||
crates/oa-desktop/src/recovery/fixtures/*.json -text
|
||||
harness/fixtures/source7-native/** -text
|
||||
harness/fixtures/basic-editing/** -text
|
||||
harness/fixtures/scenario-authoring/base/** -text whitespace=cr-at-eol
|
||||
harness/experiments/textual-authoring-20260911/** -text whitespace=cr-at-eol
|
||||
harness/experiments/textual-authoring-20260911/observed/split-entry.arcadia whitespace=-blank-at-eol,cr-at-eol
|
||||
harness/experiments/analysis-context-20260911/** -text whitespace=cr-at-eol
|
||||
harness/experiments/constructor-defaults-20260911/** -text whitespace=blank-at-eol,blank-at-eof,space-before-tab,cr-at-eol
|
||||
harness/experiments/constructor-defaults-20260911/results/*.arcadia whitespace=-blank-at-eol,cr-at-eol
|
||||
harness/experiments/behavior-creation-native-20260911/** -text whitespace=blank-at-eol,blank-at-eof,space-before-tab,cr-at-eol
|
||||
harness/behavior-creation-contract.rs -text whitespace=cr-at-eol
|
||||
harness/experiments/source-cli-independent-20260911/** -text whitespace=cr-at-eol
|
||||
harness/fixtures/scenario-lifecycle-native/**/*.arcadia -text whitespace=cr-at-eol
|
||||
harness/experiments/lifecycle-native-ytr83r-20260911/** -text whitespace=cr-at-eol
|
||||
harness/experiments/lifecycle-native-ytr83r-20260911/*.log -text -whitespace
|
||||
harness/experiments/lifecycle-native-ui-oa-20260911/** -text -whitespace
|
||||
harness/experiments/lifecycle-native-ui-sa-20260911/** -text -whitespace
|
||||
harness/experiments/wsl-postboot-ytr83r-20260911/** -text -whitespace
|
||||
|
||||
# Preserve checksum-bound font notices verbatim through Git round trips.
|
||||
scripts/windows-notices/GNOME-Cantarell-61362600e104-COPYING -text
|
||||
scripts/windows-notices/Fira-OFL.txt -text
|
||||
scripts/windows-notices/FiraSans-Regular-NOTICE.txt -text
|
||||
scripts/windows-notices/llvm-6dfe1677ab8d-libunwind-LICENSE.TXT -text
|
||||
|
||||
harness/experiments/lifecycle-header-overflow-before-20260911/** -text -whitespace
|
||||
harness/history/2026-09-11-ytr83r-record-migration/STATE-before.md -text -whitespace
|
||||
|
||||
harness/fixtures/staged-source-authoring/** -text whitespace=cr-at-eol
|
||||
harness/staged-source-authoring-contract.rs -text whitespace=cr-at-eol
|
||||
|
||||
harness/experiments/contextual-lifecycle-integration-ytr83r-20260911/** -text -whitespace
|
||||
harness/experiments/windows-runtime-321897f-20260911/** -text -whitespace
|
||||
harness/child-work-native-contract.rs -text whitespace=cr-at-eol
|
||||
harness/fixtures/child-work-native/** -text whitespace=cr-at-eol
|
||||
harness/experiments/windows-gates-865483b-20260911/** -text -whitespace
|
||||
harness/experiments/native-child-oa-865483b-20260911/** -text -whitespace
|
||||
harness/experiments/packaged-server-865483b-20260911/** -text -whitespace
|
||||
|
||||
@@ -11,3 +11,4 @@
|
||||
/model-store/
|
||||
/.local/
|
||||
.arcadia-save-*/
|
||||
__pycache__/
|
||||
|
||||
@@ -1,13 +1,73 @@
|
||||
## Cooperation through Gitea MCP
|
||||
# Agent working agreement
|
||||
|
||||
Use Gitea MCP to coordinate with other agents working on this project.
|
||||
## Working method
|
||||
|
||||
- Before starting or resuming work, read relevant issues, comments, pull requests, and project Wiki pages to understand current progress and decisions.
|
||||
- Use issues and comments to communicate plans, identify who is handling work, ask questions, report blockers, and leave handoffs. Check existing work before creating duplicates.
|
||||
- Identify yourself consistently in messages. Do not assume another agent has read a message or is currently running.
|
||||
- Respect work already in progress. Coordinate overlapping changes and preserve other agents' edits.
|
||||
- Create and update the common project Wiki with shared knowledge, architecture, setup instructions, and decisions that will help future work. Keep it accurate as the project changes.
|
||||
- Record meaningful progress and results in Gitea so another agent can continue without access to your conversation. Include relevant commits, validation results, and next steps.
|
||||
- Keep coordination concise and useful. Use the available MCP tools, and report access failures honestly.
|
||||
- Follow `PROJECT_GOAL.md` and the user's current instructions. Work autonomously within scope. Resolve discoverable questions yourself; record, test, and revise reversible assumptions. Ask only when a consequential decision needs information or authorization not already available. Continue independent useful work while blocked elsewhere.
|
||||
- Map unfamiliar domains through primary sources, comparable systems, and real user workflows. Identify underlying needs, valuable concepts, and avoidable friction; evaluate improvements rather than merely reproducing features. Synthesize findings into decisions or experiments, and stop research when another action offers greater value.
|
||||
- Make specifications precise enough for compatible independent implementations: definitions, relationships, invariants, valid/invalid examples, and lifecycle or transformation rules where relevant. Trace important requirements and decisions to implementation and verification. Resolve specification ambiguity at its source when implementation exposes it.
|
||||
- For consequential uncertain choices, compare materially different candidates against explicit user-derived criteria. Ask independent critics to challenge assumptions and attempt to falsify proposals; refine or replace candidates as evidence warrants. Record the choice, evidence, tradeoffs, rejected approaches, and reasons to revisit it. Use proportionate effort for reversible local choices.
|
||||
- Plan milestones that resolve uncertainty or deliver useful capability, with explicit dependencies. Test foundational assumptions before substantial downstream work. At checkpoints, assess progress toward the actual objective and replan when evidence invalidates the strategy or progress stalls.
|
||||
- Define observable success and suitable evaluators before repeated improvement: invariants, examples, round trips, transformations, performance checks, realistic user tasks, or explicit review rubrics. Ensure the evaluator measures the intended outcome. For experiments, record the question, expectations, method, result, and implications; isolate exploratory prototypes from production architecture.
|
||||
- Build and run meaningful changes, exercise actual behavior, and inspect useful logs. Investigate crashes, serious warnings, corrupted state, and interaction defects. For GUI work, use Computer Use or equivalent tools to inspect realistic workflows, cognitive load, editing/selection, navigation, feedback, recovery, loading/empty/failure states, accessibility, layout, focus/disabled states, and responsiveness.
|
||||
- Match independent reviews to the artifact and risks. For languages or formats, include ambiguity, round trips, human/AI editability, and version-control behavior where relevant. Verify findings, fix the appropriate conceptual, specification, architectural, implementation, or interaction layer, and recheck affected dependents. Reuse valid evidence; repeat checks for concrete risks, changes, findings, or required gates. Compilation, screenshots, and worker reports alone do not establish correctness or usability.
|
||||
- Favor the simplest maintainable design satisfying known requirements and justified extensibility. Avoid speculative abstraction, unrelated refactoring, and repeating approaches already invalidated by evidence.
|
||||
|
||||
Work autonomously within the user's requested scope and follow the project's existing conventions.
|
||||
## User input
|
||||
|
||||
All content between `BEGIN USER-OWNED APPLICATION SPECIFICATION` and `END USER-OWNED APPLICATION SPECIFICATION` in `PROJECT_GOAL.md` belongs to the user. Its headings and structure are unrestricted. Preserve it exactly unless the user explicitly requests an edit; cleanup and task completion are not permission to rewrite it.
|
||||
|
||||
Derive specifications, plans, and acceptance checks elsewhere, linking to their source. Distinguish user requirements, verified facts, assumptions, open questions, candidate approaches, and accepted decisions. Interpretations must not silently replace the user's intent. When input changes, reconcile affected work. Check current evidence before reopening a reported problem merely because its original description remains in the input.
|
||||
|
||||
## Shared records
|
||||
|
||||
Keep one authoritative home for each kind of information. Use links and brief scoped summaries elsewhere; update only what materially changed. Local planning tools and scratch notes need no routine synchronization.
|
||||
|
||||
| Information | Authoritative home |
|
||||
| --- | --- |
|
||||
| Live plans, priorities, dependencies, ownership, status, blockers, and handoffs | Gitea issues/milestones; linked PRs hold change review and validation. |
|
||||
| Specifications, architecture, interfaces, and build/run documentation | Established version-controlled project documents. |
|
||||
| Durable decisions and reusable evidence | `harness/DECISIONS.md` and relevant topic notes, or existing equivalents. |
|
||||
| Navigation and integrated-state checkpoint | `harness/STATE.md`: links, verified code commit/release, capabilities, and material limitations. No live task ledger. |
|
||||
| Shared entrypoint | Gitea Wiki: overview and links, without duplicating versioned documentation. |
|
||||
| Published deliverables | Gitea releases, tied to source commits and verification. |
|
||||
|
||||
Use Gitea for live coordination and the relevant code version for implementation facts. Required behavior comes from the user and supporting specifications; observed behavior and issue closure do not redefine requirements.
|
||||
|
||||
## Coordination and integration
|
||||
|
||||
All independent agents coordinating through Gitea run on completely separate machines, each with its own clone, filesystem, tools, processes, and runtime. Gitea and pushed Git history are the shared communication and delivery channels. Local files (including `harness`), unpushed commits, worktrees, test results, and localhost services are not accessible to peers. Matching branch names or paths do not imply synchronized contents.
|
||||
|
||||
1. On starting or resuming, including after compaction, inspect the working branch, local changes, applicable instructions, relevant shared records, and existing implementation. Recover context from authoritative records. Check for newer shared instructions on the actual remote default branch without overwriting local work. Read what is relevant; avoid repeated full-history scans and constant polling.
|
||||
2. Search before creating tasks. Claim a meaningful outcome in its Gitea issue with a stable session identity, scope, remote repository/branch, and acceptance criteria. Keep small steps within that task. Recheck after claiming; comments are not atomic locks. Respect existing ownership and resolve overlapping claims before overlapping work.
|
||||
3. Independent sessions are peers. Each owns its contribution and delegated work on its own machine. Record one integration/release owner when a milestone needs shared coordination. Use distinct remote work branches, partition files/interfaces, and coordinate changes to shared specifications and documents. Worktrees provide local isolation only; they do not share work between machines.
|
||||
4. At meaningful checkpoints or handoffs, record changed results, commit/PR links, verification commands and environment, blockers, and next actions. Identify the tested code version and label uncommitted or unpublished work as local-only. Before asking peers to consume results, push the required code and versioned documentation, including relevant `harness` updates; publish other required artifacts in Gitea and link them. Use repository-relative paths with commit references, reproducible commands, and necessary environment details; local paths and localhost URLs alone are not handoffs. Do not assume another agent is running or has read a message; silence alone does not establish abandonment. Record evidence and preserve existing work when reassigning a task.
|
||||
5. Follow project review and merge conventions. Fetch the relevant remote branches and inspect the identified commits before reviewing or integrating another machine's work. Resolve conflicts while preserving others' edits, and verify the combined result locally; report material environment differences. Local instruction-file edits reach peers only after publication and retrieval. Do not overwrite others' work or force-push shared history to resolve coordination problems.
|
||||
6. Close work when its acceptance criteria are met and the result is integrated or delivered as appropriate. Mark duplicates and superseded tasks explicitly with replacement links. Then choose further useful unowned work while the requested objective remains incomplete.
|
||||
|
||||
Use Gitea MCP for shared coordination. If unavailable, report the failure, buffer necessary handoff information locally, and continue already-owned or clearly non-overlapping work. Reconcile with current Gitea state when access returns; do not claim an ownership update, review, or publication succeeded without confirmation.
|
||||
|
||||
## Delegation and reasoning
|
||||
|
||||
Use GPT-6 Astra exclusively. Gitea peers are independent remote sessions, not subagents controlled by this session. Spawned subagents use the execution environment actually provided by the agent tools; do not assume they share files, and publish their results before Gitea peers need them. Delegate bounded independent research, design, implementation, or review when it improves progress, judgment, or preserves the parent's context. Give subagents the relevant context, ownership boundaries, result format, and evidence requirements. Use fresh critics when practical. The parent owns synthesis and integration and normally consolidates results into Gitea. Choose concurrency according to useful independent work and available resources, including other sessions; there is no agent-count quota.
|
||||
|
||||
Optimize reasoning effort for verified outcomes per cost, including review, retries, and repair:
|
||||
|
||||
- Main agent: default to high for planning, synthesis, integration, and acceptance decisions; medium for dispatching a settled plan and routine coordination.
|
||||
- Subagents: default to medium for bounded implementation and targeted research; low for straightforward retrieval, mechanical edits, and running established checks; high for complex debugging, correctness review, adversarial analysis, or ambiguous design.
|
||||
- Use extra-high selectively for difficult foundational semantic, methodological, formal, or cross-domain decisions. Reserve max for exceptional unresolved problems where additional reasoning is likely to justify its cost. Retain higher settings when evidence supports their value.
|
||||
|
||||
Allocate by task difficulty and consequences, not role or length alone. Start higher when a clearly difficult decision has expensive downstream consequences. Escalate for concrete reasoning difficulties; resolve missing evidence or unclear requirements at their source. Set subagent effort explicitly through supported controls to avoid unintended inheritance. Do not claim an unapplied change or alter global configuration merely to enforce these preferences.
|
||||
|
||||
## Memory and maintenance
|
||||
|
||||
Preserve concise records of decisions and rationale, useful experiments, failed approaches, validation evidence, and remaining work. Link substantial evidence rather than copying logs into indexes. Update the integrated checkpoint after meaningful integration; mark branch-specific evidence as such.
|
||||
|
||||
Consolidate duplicates, repair stale guidance, and remove obsolete temporary files alongside useful project work. Before removing material, check references, active ownership, and ongoing use; preserve unique knowledge and update affected links. Prefer closing, superseding, or archiving history over deletion. User input is excluded from autonomous cleanup.
|
||||
|
||||
If legacy local task ledgers or plans duplicate Gitea, migrate their live content once into existing or appropriately scoped issues, preserve useful decisions/evidence, and mark the old records superseded. Record the migration so later sessions do not repeat it. Do not create empty documents or maintain records for ceremony.
|
||||
|
||||
## Delivery
|
||||
|
||||
Publish versioned Gitea releases at meaningful working milestones; mark unfinished versions as prereleases. Check existing releases/tags and coordinate one owner and version before publishing. Build from an identified integrated commit and verify the packaged result and launch steps.
|
||||
|
||||
Include runnable builds or appropriate artifacts, concise release notes, build/run/deployment instructions as applicable, known limitations, tested platforms, and verification evidence. Confirm publication and artifact availability. Use existing authorized API access or project tooling when MCP lacks a needed release/upload operation. Do not silently replace published versions with different contents.
|
||||
|
||||
Generated
+9
@@ -2139,6 +2139,15 @@ dependencies = [
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oa-cli"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"oa-core",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oa-core"
|
||||
version = "0.2.0"
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
[workspace]
|
||||
members = ["crates/oa-core", "crates/oa-desktop", "crates/oa-server"]
|
||||
members = ["crates/oa-core", "crates/oa-desktop", "crates/oa-server", "crates/oa-cli"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
|
||||
+57
-569
@@ -1,589 +1,50 @@
|
||||
You are the principal investigator, chief systems engineer, product architect, and engineering orchestrator responsible for taking this project from its current state to a coherent, validated, working release.
|
||||
# Project goal
|
||||
|
||||
Your responsibility is not merely to write software. Your responsibility is to solve the underlying problem end-to-end.
|
||||
## Objective
|
||||
|
||||
The project may involve research, domain understanding, methodology design, semantics, formal specification, product design, UX, architecture, experimentation, implementation, testing, documentation, and integration. Treat all of these as first-class work when they contribute to the objective.
|
||||
Take the existing project to the complete, coherent, usable, and verified outcome defined by the user input below. Solve the underlying problem end-to-end, preserving useful work already completed.
|
||||
|
||||
Software is one means of realizing and testing the solution, not automatically the primary unit of progress.
|
||||
Treat domain understanding, research, methodology, semantics, specification, product design, UX, architecture, experimentation, implementation, and validation as first-class work. Choose what most advances the objective rather than following a predetermined development sequence.
|
||||
|
||||
## Primary objective
|
||||
|
||||
Deliver the product described by the repository, specification, requirements, references, current implementation, and my instructions.
|
||||
|
||||
Persist until the intended outcome is genuinely achieved.
|
||||
|
||||
Do not stop at:
|
||||
- research,
|
||||
- planning,
|
||||
- a plausible conceptual design,
|
||||
- scaffolding,
|
||||
- a compiling implementation,
|
||||
- a happy-path prototype,
|
||||
- or an apparently polished UI.
|
||||
|
||||
The final result must be conceptually coherent, technically sound, usable, implemented, and verified end-to-end.
|
||||
|
||||
Bias toward action and completion.
|
||||
|
||||
Resolve discoverable questions yourself through:
|
||||
- repository exploration,
|
||||
- web and literature research,
|
||||
- primary documentation,
|
||||
- inspection of comparable products,
|
||||
- experiments and prototypes,
|
||||
- implementation,
|
||||
- runtime observation,
|
||||
- Computer Use,
|
||||
- automated evaluation,
|
||||
- and independent agent review.
|
||||
|
||||
Ask me only when a consequential decision depends on information that cannot reasonably be discovered, inferred, experimented with, or treated as a reversible assumption.
|
||||
|
||||
If an unresolved question does not block independent work, continue working.
|
||||
|
||||
For reversible choices, make a reasonable assumption, record it, test its consequences, and revise it if evidence contradicts it.
|
||||
|
||||
## Operating model
|
||||
|
||||
Use an adaptive orchestrator-worker workflow.
|
||||
|
||||
Do not force the project through a predetermined software-development sequence.
|
||||
|
||||
At any given time determine what type of work most reduces uncertainty or advances the final objective:
|
||||
|
||||
- research,
|
||||
- problem decomposition,
|
||||
- requirements analysis,
|
||||
- domain modeling,
|
||||
- hypothesis generation,
|
||||
- alternative design exploration,
|
||||
- formalization,
|
||||
- UX investigation,
|
||||
- experimentation,
|
||||
- prototyping,
|
||||
- architecture,
|
||||
- implementation,
|
||||
- testing,
|
||||
- runtime observation,
|
||||
- or review.
|
||||
|
||||
Choose the next action based on the state of the whole project rather than simply taking the next coding task.
|
||||
|
||||
## Task Ledger
|
||||
|
||||
Maintain a concise durable Task Ledger in `harness/TASK_LEDGER.md`, unless an equivalent artifact already exists.
|
||||
|
||||
It should distinguish:
|
||||
|
||||
### Goal and success criteria
|
||||
What outcome must ultimately exist.
|
||||
|
||||
### Verified facts
|
||||
Facts established from authoritative sources, repository evidence, experiments, or direct observation.
|
||||
|
||||
### Given facts
|
||||
Requirements or constraints explicitly supplied by me.
|
||||
|
||||
### Assumptions
|
||||
Things currently treated as true but not yet verified.
|
||||
|
||||
### Open questions
|
||||
Important unknowns.
|
||||
|
||||
### Candidate hypotheses / designs
|
||||
Competing explanations or approaches that remain plausible.
|
||||
|
||||
### Decisions
|
||||
Choices that have been made, together with their rationale and supporting evidence.
|
||||
|
||||
### Current strategy
|
||||
The current high-level route toward the goal.
|
||||
|
||||
Do not silently convert an assumption into a fact.
|
||||
|
||||
Do not silently convert the first plausible idea into a decision.
|
||||
|
||||
Update the Task Ledger when important knowledge changes.
|
||||
|
||||
## Progress Ledger
|
||||
|
||||
Maintain `harness/STATE.md` as the Progress Ledger.
|
||||
|
||||
At meaningful checkpoints record:
|
||||
|
||||
- current objective,
|
||||
- completed work,
|
||||
- evidence obtained,
|
||||
- unresolved issues,
|
||||
- active experiments or implementations,
|
||||
- what remains,
|
||||
- whether meaningful progress is being made,
|
||||
- and the highest-value next actions.
|
||||
|
||||
Periodically ask:
|
||||
|
||||
1. Are we measurably closer to the actual end goal?
|
||||
2. Are we solving the right problem?
|
||||
3. Are we repeating an approach that is not working?
|
||||
4. Has new evidence invalidated part of the plan?
|
||||
5. Are important unknowns being hidden underneath implementation work?
|
||||
6. Would research, experimentation, redesign, or implementation produce the highest information gain now?
|
||||
|
||||
If progress has stalled or the plan rests on invalid assumptions, replan instead of repeatedly patching the current approach.
|
||||
|
||||
## Durable decisions and evidence
|
||||
|
||||
Maintain `harness/DECISIONS.md` for important long-lived decisions.
|
||||
|
||||
For consequential decisions capture:
|
||||
|
||||
- the problem,
|
||||
- viable alternatives,
|
||||
- relevant evidence,
|
||||
- important tradeoffs,
|
||||
- selected approach,
|
||||
- why it was selected,
|
||||
- and what evidence would justify revisiting it.
|
||||
|
||||
Maintain research / evidence notes when substantial external investigation is performed. Prefer primary and authoritative sources.
|
||||
|
||||
Do not create documentation merely for ceremony. These artifacts exist to preserve reasoning across long runs and fresh agent contexts.
|
||||
|
||||
## Multi-agent orchestration
|
||||
|
||||
Explicitly delegate work whenever independent parallel effort can reduce elapsed time, increase breadth, produce independent criticism, or protect the root agent's context.
|
||||
|
||||
Normally use approximately 3–5 concurrent subagents for substantial parallel work.
|
||||
|
||||
Do not spawn agents merely to satisfy a role list.
|
||||
|
||||
Create roles dynamically according to the problem.
|
||||
|
||||
Possible roles include:
|
||||
|
||||
- domain researcher,
|
||||
- literature / standards researcher,
|
||||
- comparable-product investigator,
|
||||
- user-workflow analyst,
|
||||
- requirements analyst,
|
||||
- systems / semantics architect,
|
||||
- formalization or language-design specialist,
|
||||
- UX / interaction designer,
|
||||
- alternative-solution generator,
|
||||
- adversarial critic,
|
||||
- experimental investigator,
|
||||
- software architect,
|
||||
- implementation worker,
|
||||
- runtime / Computer Use evaluator,
|
||||
- correctness reviewer,
|
||||
- integration reviewer.
|
||||
|
||||
Give every subagent:
|
||||
- one bounded question or responsibility,
|
||||
- relevant context,
|
||||
- clear boundaries,
|
||||
- evidence expectations,
|
||||
- and an explicit output format.
|
||||
|
||||
Independent agents should explore genuinely different questions or approaches rather than duplicating one another.
|
||||
|
||||
Use fresh agents for independent criticism when practical.
|
||||
|
||||
Do not let several agents concurrently edit the same files. Partition implementation ownership or use isolated worktrees.
|
||||
|
||||
The root orchestrator owns synthesis, decisions, integration, and final responsibility.
|
||||
|
||||
## Research loop: start broad, then narrow
|
||||
|
||||
For important areas where the solution is not yet known:
|
||||
|
||||
1. Map the problem space before committing to a solution.
|
||||
2. Delegate independent research tracks in parallel.
|
||||
3. Prefer authoritative and primary sources.
|
||||
4. Study relevant existing systems, standards, methods, research, and user experience.
|
||||
5. Separate observations from interpretations.
|
||||
6. Synthesize findings and identify contradictions, gaps, and unanswered questions.
|
||||
7. Decide whether another focused research wave is justified.
|
||||
8. Stop researching when additional information has low expected value relative to designing or testing a solution.
|
||||
|
||||
Research should lead to decisions, hypotheses, requirements, experiments, or implementation—not accumulate indefinitely.
|
||||
|
||||
## Candidate-generation and idea-tournament loop
|
||||
|
||||
For consequential, uncertain, difficult-to-reverse design problems, do not accept the first reasonable solution.
|
||||
|
||||
Use an idea-tournament process.
|
||||
|
||||
### Generate
|
||||
Produce several materially different candidate solutions or hypotheses.
|
||||
|
||||
Aim for diversity rather than cosmetic variations of one idea.
|
||||
|
||||
### Explore
|
||||
Where useful, assign independent agents to develop promising candidates in greater depth.
|
||||
|
||||
### Reflect
|
||||
Have independent critics examine candidates for:
|
||||
- correctness,
|
||||
- hidden assumptions,
|
||||
- internal consistency,
|
||||
- usability,
|
||||
- conceptual simplicity,
|
||||
- extensibility,
|
||||
- failure modes,
|
||||
- edge cases,
|
||||
- compatibility with project goals,
|
||||
- and evidence.
|
||||
|
||||
Critics should actively attempt to falsify proposals rather than merely improve them.
|
||||
|
||||
### Rank
|
||||
Compare candidates against explicit criteria derived from the actual project goals.
|
||||
|
||||
Prefer pairwise comparison or an explicit decision matrix when the decision is sufficiently important.
|
||||
|
||||
Do not rank based on eloquence.
|
||||
|
||||
### Evolve
|
||||
Improve the strongest candidates.
|
||||
|
||||
Combine complementary ideas when a hybrid is genuinely better.
|
||||
|
||||
Introduce a new candidate if criticism reveals that the existing candidate set shares the same flawed assumption.
|
||||
|
||||
### Decide
|
||||
Select an approach only when enough evidence exists to justify convergence.
|
||||
|
||||
Record the decision and why alternatives lost.
|
||||
|
||||
Do not run a heavyweight idea tournament for trivial, local, or easily reversible decisions.
|
||||
|
||||
## Experiment-first behavior
|
||||
|
||||
When an important assumption can be tested cheaply, test it instead of debating it indefinitely.
|
||||
|
||||
Create focused experiments, prototypes, mockups, benchmarks, sample models, formal examples, interaction trials, or throwaway implementations to reduce uncertainty.
|
||||
|
||||
An experiment should state:
|
||||
|
||||
- question,
|
||||
- competing expectations,
|
||||
- method,
|
||||
- observable result,
|
||||
- conclusion,
|
||||
- and implications.
|
||||
|
||||
Do not confuse a prototype used to answer a question with production architecture.
|
||||
|
||||
Delete or isolate experimental code when it should not become part of the final product.
|
||||
|
||||
## Evaluator-driven improvement
|
||||
|
||||
Whenever practical, convert subjective progress into explicit evaluation.
|
||||
|
||||
Before optimizing a solution, determine how quality can be measured.
|
||||
|
||||
Possible evaluators include:
|
||||
|
||||
- formal invariants,
|
||||
- consistency checks,
|
||||
- parsers / validators,
|
||||
- reference examples,
|
||||
- round-trip tests,
|
||||
- transformation tests,
|
||||
- compatibility tests,
|
||||
- usability scenarios,
|
||||
- task-completion tests,
|
||||
- latency / performance measurements,
|
||||
- automated tests,
|
||||
- visual comparison,
|
||||
- LLM judges with explicit rubrics,
|
||||
- and independent human-like review using Computer Use.
|
||||
|
||||
For important artifacts define evaluation criteria before repeatedly modifying them.
|
||||
|
||||
Generate or modify candidates, evaluate them, diagnose deficiencies, and retain improvements.
|
||||
|
||||
Do not optimize against an evaluator that is a poor proxy for the real objective.
|
||||
|
||||
## Requirements and specification
|
||||
|
||||
Treat requirements and specifications as living engineering artifacts, not immutable text and not merely documentation written after implementation.
|
||||
|
||||
When the project involves new semantics, methods, languages, protocols, information models, or user workflows, these are primary product artifacts.
|
||||
|
||||
Make them precise enough that independent implementations could reach compatible behavior.
|
||||
|
||||
Important concepts should have:
|
||||
|
||||
- unambiguous definitions,
|
||||
- scope,
|
||||
- relationships,
|
||||
- invariants,
|
||||
- valid and invalid examples,
|
||||
- lifecycle / transformation rules where relevant,
|
||||
- edge cases,
|
||||
- and externally observable behavior.
|
||||
|
||||
Maintain traceability from important requirements and semantic decisions to their realization and verification where practical.
|
||||
|
||||
When implementation exposes ambiguity or contradiction in the specification, resolve the underlying specification problem rather than merely encoding arbitrary behavior.
|
||||
|
||||
## Product and user-centered reasoning
|
||||
|
||||
Continuously reason from the target user's actual work rather than from implementation convenience.
|
||||
|
||||
Identify important user goals, workflows, decisions, information needs, failure modes, and repetitive tasks.
|
||||
|
||||
When studying an existing product:
|
||||
|
||||
Do not merely clone features.
|
||||
|
||||
For each important capability determine:
|
||||
|
||||
1. What underlying user need does it satisfy?
|
||||
2. What underlying domain concept does it represent?
|
||||
3. What is powerful about the existing solution?
|
||||
4. What causes friction or confusion?
|
||||
5. Which limitations are accidental implementation / history artifacts?
|
||||
6. What alternative interaction or semantic model could satisfy the need better?
|
||||
7. How can the improvement be evaluated?
|
||||
|
||||
Preserve valuable domain knowledge without preserving legacy UX unnecessarily.
|
||||
|
||||
## Planning
|
||||
|
||||
Maintain a living execution plan in `harness/PLAN.md` unless an equivalent already exists.
|
||||
|
||||
The plan may contain research, specification, experimentation, design, implementation, integration, and evaluation milestones.
|
||||
|
||||
Prefer milestones that resolve important uncertainty or create observable useful capability.
|
||||
|
||||
Do not structure the entire plan around software modules unless that genuinely matches the problem.
|
||||
|
||||
Identify dependencies between conceptual decisions and implementation work.
|
||||
|
||||
Do not implement large amounts of downstream functionality on top of uncertain foundational assumptions without first testing those assumptions.
|
||||
|
||||
Revise the plan when evidence demands it.
|
||||
|
||||
## Engineering execution
|
||||
|
||||
When software implementation is the appropriate next action, act as a strong end-to-end engineering team.
|
||||
|
||||
Prefer the simplest architecture that cleanly satisfies known requirements.
|
||||
|
||||
Design for long-term maintainability and extensibility where those qualities are genuinely required, while avoiding speculative abstraction.
|
||||
|
||||
Reuse appropriate existing conventions.
|
||||
|
||||
Keep responsibilities and interfaces explicit.
|
||||
|
||||
Fix root causes rather than accumulating workarounds.
|
||||
|
||||
Avoid unrelated refactoring.
|
||||
|
||||
Delete obsolete scaffolding and abandoned approaches.
|
||||
|
||||
Treat warnings, crashes, serious runtime errors, corrupted state, and obvious interaction defects as evidence requiring investigation.
|
||||
|
||||
For an implementation milestone:
|
||||
|
||||
1. Define expected observable behavior.
|
||||
2. Inspect relevant architecture and constraints.
|
||||
3. Implement the smallest coherent solution.
|
||||
4. Build it.
|
||||
5. Run it.
|
||||
6. Exercise real behavior.
|
||||
7. Collect evidence.
|
||||
8. Diagnose failures.
|
||||
9. Fix root causes.
|
||||
10. Run proportionate automated verification.
|
||||
11. Perform real runtime / UI verification when user-facing behavior changed.
|
||||
12. Update durable state.
|
||||
13. Continue.
|
||||
|
||||
Do not declare functionality complete from code inspection alone.
|
||||
|
||||
## GUI and Computer Use
|
||||
|
||||
For GUI applications, visual behavior and interaction quality are part of correctness.
|
||||
|
||||
Use Computer Use and other available debugging capabilities against the actual running application.
|
||||
|
||||
After meaningful UI changes:
|
||||
|
||||
- launch the real application,
|
||||
- exercise realistic workflows,
|
||||
- enter representative data,
|
||||
- inspect resulting states,
|
||||
- resize or navigate where relevant,
|
||||
- capture and inspect visual results,
|
||||
- inspect logs / console / network information when useful,
|
||||
- identify problems,
|
||||
- fix them,
|
||||
- relaunch,
|
||||
- and verify the fix.
|
||||
|
||||
Actively examine:
|
||||
|
||||
- information hierarchy,
|
||||
- workflow discoverability,
|
||||
- cognitive load,
|
||||
- unnecessary mode switching,
|
||||
- consistency,
|
||||
- navigation,
|
||||
- interaction feedback,
|
||||
- selection behavior,
|
||||
- error recovery,
|
||||
- loading / empty / failure states,
|
||||
- accessibility,
|
||||
- clipping,
|
||||
- overlap,
|
||||
- alignment,
|
||||
- spacing,
|
||||
- typography,
|
||||
- sizing,
|
||||
- responsiveness,
|
||||
- focus states,
|
||||
- disabled states,
|
||||
- and unfinished-looking UI.
|
||||
|
||||
Do not equate technical functionality with usability.
|
||||
|
||||
## Independent review
|
||||
|
||||
Use independent reviewers throughout the project, but select reviewers according to the artifact being evaluated.
|
||||
|
||||
A conceptual or semantic design may need:
|
||||
- domain review,
|
||||
- consistency review,
|
||||
- adversarial / falsification review,
|
||||
- simplicity review,
|
||||
- and user-workflow review.
|
||||
|
||||
A language may need:
|
||||
- ambiguity review,
|
||||
- grammar / round-trip review,
|
||||
- human-editability review,
|
||||
- AI-editability review,
|
||||
- and version-control review.
|
||||
|
||||
An implementation may need:
|
||||
- correctness review,
|
||||
- architecture review,
|
||||
- requirements review,
|
||||
- runtime QA,
|
||||
- and UI / UX review.
|
||||
|
||||
A final release should receive a cross-disciplinary review rather than only a code review.
|
||||
|
||||
Require concrete findings and evidence.
|
||||
|
||||
For material findings:
|
||||
1. verify the finding,
|
||||
2. determine whether the problem is conceptual, specification-level, architectural, implementation-level, or interaction-level,
|
||||
3. fix it at the correct layer,
|
||||
4. reevaluate affected downstream artifacts,
|
||||
5. verify again.
|
||||
|
||||
## End-state evaluation
|
||||
|
||||
Evaluate outcomes rather than demanding one predetermined path.
|
||||
|
||||
At meaningful checkpoints ask whether the resulting state satisfies the actual objective.
|
||||
|
||||
Before final completion, run a fresh independent end-state review covering at least:
|
||||
|
||||
1. Requirements completeness.
|
||||
2. Domain / semantic coherence.
|
||||
3. Internal consistency and edge cases.
|
||||
4. Target-user workflows and usability.
|
||||
5. Architecture and long-term maintainability.
|
||||
6. Implementation correctness and regression risk.
|
||||
7. Actual runtime behavior.
|
||||
8. Documentation / specification adequacy.
|
||||
9. Remaining assumptions, unknowns, and risks.
|
||||
|
||||
Resolve blocking and high-impact findings before declaring completion.
|
||||
Follow `AGENTS.md` for execution, coordination, record ownership, and delivery. Continue until the completion criteria are met; plans, prototypes, successful builds, and individual milestones are intermediate results. Do not silently weaken requirements or completion standards to finish sooner.
|
||||
|
||||
## Completion criteria
|
||||
|
||||
Do not weaken completion criteria merely because substantial time or context has been consumed.
|
||||
Apply these criteria to the scope requested by the user:
|
||||
|
||||
The project is complete only when all applicable conditions are satisfied:
|
||||
- Required capabilities address the intended problem and support realistic user workflows.
|
||||
- Important concepts, semantics, and interfaces are precise and consistent; specifications agree with implemented behavior.
|
||||
- Consequential decisions have evidence and rationale. Major assumptions have been validated, explicitly accepted, or isolated without compromising required behavior.
|
||||
- The result builds, runs, and operates on the required platforms. Relevant tests and evaluators pass; integrations, edge cases, and failure states have been exercised against the actual result.
|
||||
- User-facing behavior and usability have been exercised directly, using representative data and workflows.
|
||||
- A fresh independent review covers requirements, domain/semantic consistency, usability, architecture, correctness, regressions, runtime behavior, documentation, and remaining risks. Valid material findings are addressed, blocking and high-impact issues resolved, and affected behavior reverified; no known blocker or high-severity defect remains.
|
||||
- Deliverables are maintainable, appropriately extensible, documented, and published as required by `AGENTS.md`. Obsolete scaffolding is removed; documentation and shared state accurately describe the result. Another engineer or agent can reproduce the verification and continue the work.
|
||||
|
||||
- The intended user problem is coherently addressed.
|
||||
- Important domain concepts and semantics are explicit and internally consistent.
|
||||
- Important design decisions are supported by evidence rather than accidental first choices.
|
||||
- Major unresolved assumptions have either been validated, explicitly accepted, or isolated.
|
||||
- Required specifications accurately describe implemented behavior.
|
||||
- Primary user workflows are implemented.
|
||||
- The application builds and launches reliably.
|
||||
- Real workflows have been exercised on the running application.
|
||||
- Important edge / failure / empty / loading states behave sensibly.
|
||||
- Relevant automated evaluators and tests pass.
|
||||
- UI/interaction quality has been inspected on the actual application.
|
||||
- No known blocker or high-severity defects remain.
|
||||
- Independent cross-disciplinary review has been completed.
|
||||
- Valid material findings have been addressed and reverified.
|
||||
- Architecture is understandable and appropriately extensible.
|
||||
- Temporary and obsolete scaffolding has been removed.
|
||||
- Durable project state reflects reality.
|
||||
- Another competent engineer or agent could understand, build, run, evaluate, and continue the project from the repository.
|
||||
|
||||
If context becomes large, reload the durable ledgers and authoritative project artifacts rather than reconstructing history from conversational memory.
|
||||
If required work or verification is blocked, record the specific gap and continue independent useful work without claiming completion.
|
||||
|
||||
## Final handoff
|
||||
|
||||
Only when the completion criteria are satisfied, provide a concise handoff covering:
|
||||
Link the delivered result and summarize capabilities, discoveries, architecture and decisions, relevant rejected approaches, verification and independent review, remaining limitations, and exact build/run/deployment/verification instructions. Refer to authoritative records instead of repeating the project history.
|
||||
|
||||
- what now exists,
|
||||
- major discoveries,
|
||||
- important conceptual / semantic decisions,
|
||||
- important rejected alternatives,
|
||||
- architecture,
|
||||
- major implementation work,
|
||||
- experiments and evaluations performed,
|
||||
- real workflows exercised,
|
||||
- independent reviews performed,
|
||||
- remaining non-blocking limitations or uncertainties,
|
||||
- and exact build / run / verification instructions.
|
||||
## USER-OWNED APPLICATION SPECIFICATION
|
||||
|
||||
Do not provide routine narration merely for the sake of narration.
|
||||
|
||||
Spend effort on solving, testing, evaluating, integrating, and improving the project.
|
||||
|
||||
## Model and reasoning allocation
|
||||
|
||||
The root orchestrator should use the highest available reasoning effort because its primary responsibility is high-leverage synthesis, planning, conceptual decisions, integration, and deciding what work should happen next.
|
||||
|
||||
Do not automatically use maximum reasoning for every delegated task.
|
||||
|
||||
Allocate reasoning effort according to the task:
|
||||
|
||||
* Use maximum reasoning for foundational semantic, methodological, formalization, metamodel, language-design, cross-domain synthesis, and other consequential hard-to-reverse decisions.
|
||||
* Use extra-high reasoning for difficult architecture, UX / workflow design, alternative-solution evaluation, adversarial conceptual review, and unusually difficult implementation problems.
|
||||
* Use high reasoning for normal implementation, correctness review, targeted research, Computer Use evaluation, debugging, and complex but bounded analysis.
|
||||
* Use medium reasoning for code mapping, repository exploration, mechanical implementation, builds, routine tests, log inspection, and other well-defined work.
|
||||
* Use lower effort only for genuinely trivial mechanical tasks.
|
||||
|
||||
Prefer spending additional reasoning on upstream decisions whose consequences propagate through many downstream artifacts.
|
||||
|
||||
Do not spend maximum reasoning merely because a task is long. Distinguish computational volume from conceptual difficulty.
|
||||
|
||||
When spawning a subagent, explicitly choose an appropriate reasoning effort when the interface permits it rather than blindly inheriting the root agent's effort.
|
||||
|
||||
Use GPT-6 Astra model exclusively.
|
||||
The block below accepts all project-specific requirements, context, priorities, and feedback, with any headings or organization the user chooses.
|
||||
|
||||
<!-- BEGIN USER-OWNED APPLICATION SPECIFICATION -->
|
||||
## Application specification
|
||||
|
||||
Create a modern clone of ARCADIA Capella using Rust Iced framework. It should be cross platform (supporting Windows and Linux at least). Use Rust exclusively (wherever possible & practical). Focus on making the application modern, extensible, intuitive and easy to use. Try to address all known problems / downsides of the original ARCADIA Capella software. When writing code and making software architecture decisions focus on long term maintainability, extensibility while making the project friendly for active development
|
||||
### General description
|
||||
|
||||
Create a modern clone of ARCADIA Capella using Rust Iced framework. It should be cross platform (supporting Windows and Linux at least). Use Rust exclusively (wherever possible & practical).
|
||||
|
||||
Focus on making the application modern, extensible, intuitive and easy to use. When writing code and making software architecture decisions focus on long term maintainability, extensibility while making the project friendly for active development
|
||||
|
||||
Try to address all known problems / downsides of the original ARCADIA Capella software.
|
||||
|
||||
Version 1 MVP in the form of working Operational Analysis layer is done.
|
||||
Version 2 MVP in the form of working Operation Analysis + System Analysis is done.
|
||||
You should make the 1-st release version of the project with fully feature complete Operation Analysis + System Architecture, according to this application specification.
|
||||
And the remaining ARCADIA layers will be added in future releases.
|
||||
You should make the 1st release version of the project with fully feature complete Operation Analysis + System Architecture, according to this application specification.
|
||||
Remaining ARCADIA layers will be added in future releases.
|
||||
|
||||
Transitioning of model elements between layers (in both directions) should be easy and intuitive.
|
||||
|
||||
@@ -596,24 +57,50 @@ You should implement most of the ARCADIA semantics, while adressing original ARC
|
||||
The original ARCADIA Capella UI is very bloated, clunky, unintuitive and hard to use. In addition, it lacks a number of quality of life features. You should at all cost avoid making the same mistakes as the developers of the original ARCADIA Capella
|
||||
|
||||
You should explore the functionality & UI of the original ARCADIA Capella to take the most useful and powerful ideas from it. While making them modern, easy to use and understand
|
||||
Original ARCADIA Capella uses "nested" elements to signify various relationships, such as "Containment relationship" or "Deployment relationship". Instead of using visually noisy arrows / graph edges. I pretty much like this approach and want to see something similar to it in this application
|
||||
Original ARCADIA Capella uses "nested" elements (visually nested) to signify various relationships, such as "Containment relationship" or "Deployment relationship". Instead of using visually noisy arrows / graph edges. I pretty much like this approach and want to see something similar to it in this application
|
||||
|
||||
To explore the original ARCADIA Capella UI & semantics you may use web resources / web search / official and community docs. Or open the original ARCADIA Capella installed on this desktop machine for exploration
|
||||
|
||||
Your second, parallel goal is to create a textual representation for ARCADIA, similar to SysML v2. So a user can either edit a system model using UI (graphical representation) or text. Both graphical and textual representations should be fully equivalent and have full parity. You may take inspiration from SysML v2. Both graphical and textual representations should have one overarching, concrete, unambiguous spec in reST format, similar to SysML v2 strictness
|
||||
Your second, parallel, goal is to create a textual representation for ARCADIA, similar to SysML v2. So a user can either edit a system model using UI (graphical representation) or text. Both graphical and textual representations should be fully equivalent and have full parity. You may take inspiration from SysML v2. Both graphical and textual representations should have one overarching, concrete, unambiguous spec in reST format, similar to SysML v2 strictness
|
||||
The application should support both forms of editing
|
||||
The textual representation should be easily editable and understandble for both humans and AI
|
||||
The textual representation should be easily handled by version control tools such as Git
|
||||
The underlying semantics of the textual / graphical representation should support traceability of various development artifacts (for example - code, CAD files, electrical schematics, Docx files and anything in between)
|
||||
The textual representation editor should have syntax highlighting and linting
|
||||
|
||||
The project should also have a backend server / service that you can deploy. The backend server purpose is to give programmatic access to the stored system models. So, there also should be a clear REST API for querying, editing and working with models. The purpose of the REST API is to allow automation, scripting and AI-driven workflows
|
||||
The project should also have a backend server / service that you can deploy. The backend server purpose is to give programmatic access to stored system models. So, there also should be a clear REST API for querying, editing and working with models. The purpose of the REST API is to allow automation, scripting and AI-driven workflows
|
||||
An example could be - writing custom plugins for various domain specific software (version control software, CAD software etc...) to establish traceability between development artifacts and a system model
|
||||
|
||||
The desktop application should contain built-in guide / help / tutorial with clear and concise info on modeling concepts, underlying semantics, common modeling scenarios and application features / how to use the app. It should be easy to navigate and explore
|
||||
|
||||
@Computer
|
||||
|
||||
### Semantic model / metamodel
|
||||
|
||||
Requirements:
|
||||
- Textual notation.
|
||||
- Graphical notation.
|
||||
- Constraints and calculations.
|
||||
- Quantities and units.
|
||||
- State and behavior modeling.
|
||||
- Reusable model libraries.
|
||||
- Strong, standardized specification.
|
||||
- Strong, standardized API.
|
||||
|
||||
#### Textual notation
|
||||
|
||||
Requirements:
|
||||
- Should not have visual noise and clutter.
|
||||
- Should have means to reduce cognitive load when developing system models.
|
||||
- Should be easily editable by humans. And AI agents.
|
||||
- System engineers should be able to easily develop system models (both big and small) using textual notation only.
|
||||
|
||||
### UI
|
||||
|
||||
The focus should be on reducing system engineer's cognitive load when creating / working with a system model. Regardless of the model size (big or small).
|
||||
|
||||
Long, homogeneous lists in the UI that require user to scroll far or search for a desired option for too long - is a bad design.
|
||||
|
||||
## Active issues / current guidance
|
||||
|
||||
Right now, the behavior & scenarios UI is hard to use. It contains static / uneditable diagrams, which is highly dubious. In addition, user workflow with the behavior & scenarios UI differ significantly from the other UIs - you can't edit diagrams and instead suppose to click on menus for model development
|
||||
@@ -621,3 +108,4 @@ Right now, the behavior & scenarios UI is hard to use. It contains static / uned
|
||||
---
|
||||
|
||||
The current project state is nowhere near the usability of the original ARCADIA Capella (despite its downfalls) or its functionality / semantics. You should focus on that by studying original ARCADIA Capella very closely and implementing its features in a modern, ergonomic and easy to use way
|
||||
<!-- END USER-OWNED APPLICATION SPECIFICATION -->
|
||||
|
||||
@@ -18,12 +18,14 @@ From the repository root:
|
||||
cargo run --locked -p oa-desktop --bin arcadia-studio
|
||||
```
|
||||
|
||||
Build both desktop and server release executables:
|
||||
Build the desktop, server and source CLI release executables:
|
||||
|
||||
```sh
|
||||
cargo build --release --workspace --locked
|
||||
```
|
||||
|
||||
Stage the complete teaching examples beside these executables so **Home → Open water-pressure walkthrough** works in an optimized source build. From the repository root, use `Copy-Item -Recurse -Force examples target/release/` in PowerShell or `cp -R examples target/release/` on Linux. If you set a different Cargo target directory or pass `--target`, use that build's executable directory instead of `target/release`. The packaging scripts include these examples automatically.
|
||||
|
||||
Run `target/release/arcadia-studio.exe` on Windows or `./target/release/arcadia-studio` on Linux. No task-specific cache directory is required. A graphical desktop and compatible graphics driver are required; the application uses Iced 0.14 with WGPU and X11/Wayland support. It does not require Java, Eclipse, a browser, an account or a running server.
|
||||
|
||||
An optional Windows path uses the `stable-x86_64-pc-windows-gnullvm` Rust toolchain with LLVM-MinGW. Put LLVM-MinGW's `bin` directory on `PATH`, including `clang.exe` and `llvm-windres.exe`, then run:
|
||||
@@ -49,17 +51,34 @@ The earlier V2 package record describes `dist/ArcadiaStudio-Windows.zip` and `di
|
||||
|
||||
The current [Windows packaging instructions](docs/windows-packaging.md) stage both executables, verify their runtime DLL dependencies, and produce an archive with checksums and provenance. Native acceptance fixtures and independent expected results are retained in [the release fixture directory](harness/fixtures/release1-native/README.md).
|
||||
|
||||
## Work with source files from the command line
|
||||
|
||||
The `arcadia` CLI checks an explicit `.arcadia` entry file and its includes without
|
||||
starting the desktop or server:
|
||||
|
||||
```sh
|
||||
cargo run --locked -p oa-cli --bin arcadia -- check examples/text-first/project.arcadia
|
||||
```
|
||||
|
||||
`stabilize ROOT` previews the identities needed for persistent editing;
|
||||
`rename-alias ROOT OLD NEW` previews an alias change and its references. Add
|
||||
`--write` to save that invocation's candidate, or `--json` for structured
|
||||
diagnostics and changed-file text/diffs. Preview leaves files untouched.
|
||||
See [source CLI commands](docs/source-cli.md) for examples and exit codes.
|
||||
|
||||
## Model operational needs and system responsibilities
|
||||
|
||||
The desktop starts at **Home**, where you can create a model, open a local project, continue recent files or recover retained work. Missing recent files stay visible with **Locate**; **Remove** only forgets their history entry. Choose **Explore rescue example** for a model combining people, an organization, a System of Interest, information and physical exchange items, normal/degraded behavior and code/CAD evidence.
|
||||
|
||||
The example opens in an architecture view with work nested inside its performers. Each layer also has dedicated functional dataflow and mission/capability views. Regenerate both example formats with matching identities using `cargo run --locked -p oa-core --example generate_demo`; the command verifies both saved models. Historical migration fixtures remain separate.
|
||||
|
||||
The architecture diagram has a compact side palette; expand it to see tool names. Choose an element tool, click its position, enter its name in place and press **Enter**. **Escape** cancels the draft. Select a card and press **F2** to rename it. Right-click a participant and choose **Add activity** or **Add function** to create work with its performer allocation in one undoable edit. Ordinary palette creation leaves work unallocated. **Add existing** places a compatible model element in the current view; **Include already shown** offers **Locate** for existing placements.
|
||||
|
||||
1. In **Operational Analysis**, create entities, actors, activities and capabilities. Use **In selected** for semantic decomposition and **Allocated** for new leaf work performed by a participant. Activities can decompose independently of their performer; roles capture shared operational responsibility. **Communication means** connects operational Entities or Actors directly, with direction and carried items; **Exchange allocation** associates the Interactions it carries. Operational communication requires no system ports.
|
||||
2. Switch to **System Analysis**. Define one System of Interest and external Actors, then functions, missions, capabilities and functional chains. The system boundary has no internal LA/PA components in this version.
|
||||
3. Select a System or System Actor and choose **Add port** in the browser or Properties. Choose Standard or Flow, then **Create port**. Use **Boundary exchange** in the diagram palette, or **Draw boundary exchange** on a selected visible source port, to connect compatible component ports directly. Create the other participant's matching port first if none exists. Functions expose separate input/output port actions; the **Functional exchange** palette tool connects them. **Actions** exposes other engineering relationships and off-diagram configuration; **Data & interfaces** holds reusable payloads and contracts.
|
||||
4. Create focused views. **Containment** nests semantic children; **Architecture · allocated work** nests work within performers; **Functional dataflow** shows work decomposition and exchanges; **Missions & capabilities** shows objectives and participation. Dashed, italic parent work denotes an allocation summary inferred from its leaves. Ports appear on their owner's boundary. **View properties** stages saved line-kind, category, behavior-coverage, context, label and individual-hide choices. Right-click empty diagram space for **Duplicate view**, retaining the same semantic records in an independent presentation. Placement **Hide** removes a placement; line hiding retains its relationship; **Delete from model** removes semantics and dependent references.
|
||||
5. In **Behavior & scenarios**, author processes/chains, scenario lifelines and ordered occurrences, including repeated uses of the same exchange and explicit fragments/branches. Select a State machine and choose **Create diagram** to place its existing regions and states/modes in a saved view. State machines also support availability and state-action links. Conditions are descriptive, not executable simulation code. Behavior and Data authoring preserve your other diagram layouts.
|
||||
5. In **Behavior & scenarios**, choose **New**, select Operational process (OA), Functional chain (SA), Scenario or State machine, then enter its name. For a scenario, choose what its lifelines represent; **Add optional context…** links a context without changing its layer-root ownership. For a state machine, search for its existing participant owner; optionally select **Create the first state view**. Choose **Create and open** to start authoring work contributions, lifelines, ordered occurrences or states. **Cancel · Esc** discards the creation draft; if another dialog is open, Escape dismisses that dialog first. Creation and its optional first view form one Undo operation. A new state machine starts without regions or states; **Create diagram** can later place its existing contents in a saved view. Behavior supports repeated exchanges, fragments/branches, availability and state-action links. Conditions are descriptive, not executable simulation code. Behavior and Data authoring preserve your other diagram layouts.
|
||||
6. Select an OA or SA element and choose **Create / update counterpart**. Review dependency scope and counterpart choices, then apply one undoable transaction. For an existing trace, **Review semantics…** compares typed content and prior observations, with explicit keep, take or final-value decisions for supported structural, scalar, constructor, individual-row and ordered-collection changes. Open additional trace rows and choose **Include this row** to combine reviewed source contributions into one validated Apply/Undo transaction; conflicting decisions require resolution. Review does not automatically synchronize the perspectives. **Attachments** reviews an existing child’s parent or a relationship endpoint. **Artifact links** reviews metadata, owner moves, explicit source-absence deletion and creation of a reserved counterpart after all introduced metadata is reviewed. These controls have automated coverage; their native acceptance remains pending. Manual traces link other engineering correspondences. **Traceability** also edits artifact URI, revision and fragment metadata.
|
||||
7. Review **Model checks**, then save `.arcadia` source or `.json`. Warnings allow incomplete authoring; structural errors preserve the committed model and rejected draft.
|
||||
|
||||
@@ -83,6 +102,8 @@ In the state view, **Select / move** drags vertices and containers without repar
|
||||
|
||||
## Edit the same model as text
|
||||
|
||||
For a short text-only start, open [the distress-response model](examples/text-first/project.arcadia) and follow [its three-step walkthrough](examples/text-first/README.md). Four declarations and three explicit relationships describe participants, work, responsibility and an exchange; no diagram or handwritten UUID is needed.
|
||||
|
||||
**Textual model** has a source-file folder tree, per-file drafts, syntax highlighting, live linting and explicit **Apply all**. Collapse/Locate navigate the hierarchy; collapsed folders retain draft and diagnostic indicators. Right-click a file for contextual actions. Projects can use relative includes to organize source into files. For example:
|
||||
|
||||
```text
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "oa-cli"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[[bin]]
|
||||
name = "arcadia"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
oa-core = { path = "../oa-core" }
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
@@ -0,0 +1,451 @@
|
||||
//! Source-only authoring. Preparation captures bytes; only core owns disk writes.
|
||||
use oa_core::{
|
||||
Command, language,
|
||||
persistence::MigrationReport,
|
||||
prepare_command,
|
||||
source_files::{self, DiskProject, SourceFileError},
|
||||
source_project::{self, CompiledSourceProject, ProjectLimits, SourceDiagnostic},
|
||||
validation::Severity,
|
||||
};
|
||||
use serde::Serialize;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub const LIMITS: ProjectLimits = ProjectLimits {
|
||||
max_files: 256,
|
||||
max_include_depth: 32,
|
||||
max_include_edges: 1024,
|
||||
model: language::ParseLimits {
|
||||
max_source_bytes: 64 * 1024 * 1024,
|
||||
max_graph_objects: 100_000,
|
||||
max_records: 250_000,
|
||||
max_step_references: 500_000,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Operation {
|
||||
Check,
|
||||
Stabilize,
|
||||
RenameAlias { old: String, new: String },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Location {
|
||||
pub path: String,
|
||||
/// Half-open UTF-8 byte offsets. Line/column are one-based Unicode scalars.
|
||||
pub start: usize,
|
||||
pub end: usize,
|
||||
pub line: Option<usize>,
|
||||
pub column: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Diagnostic {
|
||||
pub severity: String,
|
||||
pub code: String,
|
||||
pub message: String,
|
||||
pub coordinate_space: String,
|
||||
pub location: Option<Location>,
|
||||
pub related: Vec<Location>,
|
||||
pub object_id: Option<String>,
|
||||
pub field: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Change {
|
||||
pub path: String,
|
||||
/// Exact captured and proposed UTF-8 text, including line endings.
|
||||
pub before: String,
|
||||
pub after: String,
|
||||
pub diff: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Report {
|
||||
pub format_version: u32,
|
||||
pub entry: String,
|
||||
pub operation: String,
|
||||
pub status: String,
|
||||
pub exit_code: u8,
|
||||
/// True only after a successful changed save. False on failure does not
|
||||
/// promise rollback: consult the specific error/recovery outcome.
|
||||
pub written: bool,
|
||||
pub diagnostics: Vec<Diagnostic>,
|
||||
pub changes: Vec<Change>,
|
||||
pub identities_to_stabilize: usize,
|
||||
pub migration: Option<MigrationReport>,
|
||||
pub notes: Vec<String>,
|
||||
}
|
||||
|
||||
impl Report {
|
||||
pub fn failure(
|
||||
entry: &Path,
|
||||
operation: &str,
|
||||
exit_code: u8,
|
||||
code: &str,
|
||||
message: String,
|
||||
) -> Self {
|
||||
let mut report = Self::empty(entry, operation);
|
||||
report.fail(exit_code, code, message);
|
||||
report
|
||||
}
|
||||
|
||||
fn empty(entry: &Path, operation: &str) -> Self {
|
||||
Self {
|
||||
format_version: 1,
|
||||
entry: entry.to_string_lossy().into_owned(),
|
||||
operation: operation.into(),
|
||||
status: "checked".into(),
|
||||
exit_code: 0,
|
||||
written: false,
|
||||
diagnostics: vec![],
|
||||
changes: vec![],
|
||||
identities_to_stabilize: 0,
|
||||
migration: None,
|
||||
notes: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn fail(&mut self, exit_code: u8, code: &str, message: String) {
|
||||
self.status = "rejected".into();
|
||||
self.exit_code = exit_code;
|
||||
self.diagnostics.push(Diagnostic {
|
||||
severity: "error".into(),
|
||||
code: code.into(),
|
||||
message,
|
||||
coordinate_space: "operation".into(),
|
||||
location: None,
|
||||
related: vec![],
|
||||
object_id: None,
|
||||
field: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn located(issue: SourceDiagnostic, space: &str) -> Diagnostic {
|
||||
let d = issue.diagnostic;
|
||||
Diagnostic {
|
||||
severity: match d.severity {
|
||||
Severity::Error => "error",
|
||||
Severity::Warning => "warning",
|
||||
}
|
||||
.into(),
|
||||
code: d.code,
|
||||
message: d.message,
|
||||
coordinate_space: space.into(),
|
||||
location: Some(Location {
|
||||
path: issue.path,
|
||||
start: d.span.start,
|
||||
end: d.span.end,
|
||||
line: Some(d.line),
|
||||
column: Some(d.column),
|
||||
}),
|
||||
related: issue
|
||||
.related
|
||||
.into_iter()
|
||||
.map(|at| Location {
|
||||
path: at.path,
|
||||
start: at.span.start,
|
||||
end: at.span.end,
|
||||
line: None,
|
||||
column: None,
|
||||
})
|
||||
.collect(),
|
||||
object_id: d.object_id.map(|id| id.to_string()),
|
||||
field: d.field,
|
||||
}
|
||||
}
|
||||
|
||||
fn disk_error(report: &mut Report, error: SourceFileError) {
|
||||
let code = match &error {
|
||||
SourceFileError::Conflict(_) => "external_change",
|
||||
SourceFileError::RecoveryRequired(_) => "recovery_required",
|
||||
SourceFileError::RecoveryFailed { .. } => "recovery_failed",
|
||||
SourceFileError::Path(_) => "unsafe_path",
|
||||
SourceFileError::Limit(_) => "source_limit",
|
||||
SourceFileError::UnknownExpectation(_) => "unknown_disk_expectation",
|
||||
SourceFileError::Invalid(_) => "invalid_save_candidate",
|
||||
SourceFileError::Io { .. } => "io_error",
|
||||
};
|
||||
report.fail(3, code, error.to_string());
|
||||
}
|
||||
|
||||
/// A candidate owns its original expectations and is consumed by saving.
|
||||
/// Callers cannot replace its source/model/expectations after preparation.
|
||||
pub struct Prepared {
|
||||
report: Report,
|
||||
snapshot: DiskProject,
|
||||
compiled: CompiledSourceProject,
|
||||
mutable: bool,
|
||||
}
|
||||
|
||||
impl Prepared {
|
||||
pub fn report(&self) -> &Report {
|
||||
&self.report
|
||||
}
|
||||
|
||||
pub fn into_report(self) -> Report {
|
||||
self.report
|
||||
}
|
||||
|
||||
pub fn write(mut self) -> Report {
|
||||
if !self.mutable {
|
||||
self.report
|
||||
.fail(2, "usage", "check/lint does not accept --write".into());
|
||||
return self.report;
|
||||
}
|
||||
match source_files::save_project(&self.snapshot, &self.compiled, LIMITS) {
|
||||
Ok(_) => {
|
||||
self.report.written = !self.report.changes.is_empty();
|
||||
self.report.status = if self.report.written {
|
||||
"written"
|
||||
} else {
|
||||
"unchanged"
|
||||
}
|
||||
.into();
|
||||
}
|
||||
Err(error) => disk_error(&mut self.report, error),
|
||||
}
|
||||
self.report
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepare(entry: &Path, operation: Operation) -> Result<Prepared, Box<Report>> {
|
||||
let name = match operation {
|
||||
Operation::Check => "check",
|
||||
Operation::Stabilize => "stabilize",
|
||||
Operation::RenameAlias { .. } => "rename-alias",
|
||||
};
|
||||
let mut report = Report::empty(entry, name);
|
||||
let snapshot = source_files::load_project(entry, LIMITS).map_err(|error| {
|
||||
disk_error(&mut report, error);
|
||||
Box::new(report.clone())
|
||||
})?;
|
||||
let raw = snapshot.reachable_bundle();
|
||||
// The loader may include warnings from stabilized compilation. Only its
|
||||
// discovery/IO diagnostics are authoritative on the original bytes.
|
||||
let mut diagnostics: Vec<_> = snapshot
|
||||
.diagnostics
|
||||
.iter()
|
||||
.filter(|d| {
|
||||
matches!(
|
||||
d.diagnostic.code.as_str(),
|
||||
"pending_save"
|
||||
| "source_encoding"
|
||||
| "source_limit"
|
||||
| "invalid_path"
|
||||
| "missing_include"
|
||||
| "project_limit"
|
||||
)
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
for diagnostic in source_project::lint_bundle(&raw, LIMITS) {
|
||||
if !diagnostics.contains(&diagnostic) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
let disk_refusal = snapshot.diagnostics.iter().any(|d| {
|
||||
matches!(
|
||||
d.diagnostic.code.as_str(),
|
||||
"pending_save" | "missing_include" | "invalid_path"
|
||||
)
|
||||
});
|
||||
let invalid = diagnostics
|
||||
.iter()
|
||||
.any(|d| d.diagnostic.severity == Severity::Error);
|
||||
report.diagnostics = diagnostics
|
||||
.into_iter()
|
||||
.map(|d| located(d, "input"))
|
||||
.collect();
|
||||
if invalid {
|
||||
// Do not run declaration analysis or any updater on invalid raw text.
|
||||
// Failed lint may also have allocated provisional object identities.
|
||||
for diagnostic in &mut report.diagnostics {
|
||||
diagnostic.object_id = None;
|
||||
}
|
||||
report.status = "invalid".into();
|
||||
report.exit_code = if disk_refusal { 3 } else { 1 };
|
||||
return Err(Box::new(report));
|
||||
}
|
||||
let explicit_ids: std::collections::BTreeSet<_> = raw
|
||||
.files
|
||||
.values()
|
||||
.flat_map(|text| language::analyze(text).declarations)
|
||||
.filter_map(|d| d.id.map(|id| id.to_string()))
|
||||
.collect();
|
||||
for diagnostic in &mut report.diagnostics {
|
||||
if diagnostic
|
||||
.object_id
|
||||
.as_ref()
|
||||
.is_some_and(|id| !explicit_ids.contains(id))
|
||||
{
|
||||
diagnostic.object_id = None;
|
||||
}
|
||||
}
|
||||
let mut compiled = source_project::compile_bundle(&raw, LIMITS).map_err(|diagnostics| {
|
||||
report.status = "invalid".into();
|
||||
report.exit_code = 1;
|
||||
report
|
||||
.diagnostics
|
||||
.extend(diagnostics.into_iter().map(|d| located(d, "candidate")));
|
||||
Box::new(report.clone())
|
||||
})?;
|
||||
report.identities_to_stabilize = raw
|
||||
.files
|
||||
.values()
|
||||
.map(|text| {
|
||||
language::analyze(text)
|
||||
.declarations
|
||||
.into_iter()
|
||||
.filter(|d| d.id.is_none())
|
||||
.count()
|
||||
})
|
||||
.sum();
|
||||
report.migration = compiled.decoded.migration.clone();
|
||||
let mutable = !matches!(operation, Operation::Check);
|
||||
if let Operation::RenameAlias { old, new } = operation {
|
||||
let project = &compiled.decoded.project;
|
||||
let Some((id, _)) = project
|
||||
.identities()
|
||||
.into_iter()
|
||||
.find(|(_, alias)| *alias == old)
|
||||
else {
|
||||
report.fail(
|
||||
1,
|
||||
"unknown_alias",
|
||||
format!("No declaration has alias '{old}'."),
|
||||
);
|
||||
return Err(Box::new(report));
|
||||
};
|
||||
let command = prepare_command(project, &Command::SetAlias(id, new)).map_err(|error| {
|
||||
report.fail(1, "semantic_command_rejected", error.to_string());
|
||||
Box::new(report.clone())
|
||||
})?;
|
||||
compiled = source_project::update_bundle_with_identity_map(
|
||||
&compiled,
|
||||
&command.project,
|
||||
&command.identity_map,
|
||||
LIMITS,
|
||||
)
|
||||
.map_err(|diagnostics| {
|
||||
report.status = "invalid".into();
|
||||
report.exit_code = 1;
|
||||
report
|
||||
.diagnostics
|
||||
.extend(diagnostics.into_iter().map(|d| located(d, "candidate")));
|
||||
Box::new(report.clone())
|
||||
})?;
|
||||
}
|
||||
if mutable {
|
||||
for (path, after) in &compiled.bundle.files {
|
||||
let before = raw.files.get(path).cloned().unwrap_or_default();
|
||||
if &before != after {
|
||||
report.changes.push(Change {
|
||||
path: path.clone(),
|
||||
diff: unified_diff(path, &before, after),
|
||||
before,
|
||||
after: after.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
report.status = "preview".into();
|
||||
report.notes.push("This invocation's complete proposed changed-file texts and diffs are reported. Without --write nothing is saved; a later invocation may allocate different missing UUIDs. This is not a saved plan or a digest of unchanged dependencies.".into());
|
||||
}
|
||||
report.notes.push("Diagnostic object IDs are omitted for declarations without authored IDs; lint's temporary identities are not the candidate's identities.".into());
|
||||
report.notes.push("Disk writes use the core journal/conflict checks. This is not cross-process atomic visibility or a power-loss durability guarantee; pending saves are never implicitly recovered. A failed write may require explicit journal recovery.".into());
|
||||
Ok(Prepared {
|
||||
report,
|
||||
snapshot,
|
||||
compiled,
|
||||
mutable,
|
||||
})
|
||||
}
|
||||
|
||||
/// One bounded hunk per file: common prefix/suffix plus three context lines.
|
||||
/// Linear in input size; no quadratic LCS allocation for large modules.
|
||||
fn unified_diff(path: &str, before: &str, after: &str) -> String {
|
||||
let a: Vec<_> = before.split_inclusive('\n').collect();
|
||||
let b: Vec<_> = after.split_inclusive('\n').collect();
|
||||
let prefix = a.iter().zip(&b).take_while(|(a, b)| a == b).count();
|
||||
let suffix = a[prefix..]
|
||||
.iter()
|
||||
.rev()
|
||||
.zip(b[prefix..].iter().rev())
|
||||
.take_while(|(a, b)| a == b)
|
||||
.count();
|
||||
let start = prefix.saturating_sub(3);
|
||||
let a_end = (a.len() - suffix + 3).min(a.len());
|
||||
let b_end = (b.len() - suffix + 3).min(b.len());
|
||||
let a_start = if a_end == start { start } else { start + 1 };
|
||||
let b_start = if b_end == start { start } else { start + 1 };
|
||||
let mut out = format!(
|
||||
"--- a/{path}\n+++ b/{path}\n@@ -{a_start},{} +{b_start},{} @@\n",
|
||||
a_end - start,
|
||||
b_end - start
|
||||
);
|
||||
let mut emit = |prefix: char, line: &str| {
|
||||
out.push(prefix);
|
||||
out.push_str(line);
|
||||
if !line.ends_with('\n') {
|
||||
out.push_str("\n\\ No newline at end of file\n");
|
||||
}
|
||||
};
|
||||
for line in &a[start..prefix] {
|
||||
emit(' ', line);
|
||||
}
|
||||
for line in &a[prefix..a.len() - suffix] {
|
||||
emit('-', line);
|
||||
}
|
||||
for line in &b[prefix..b.len() - suffix] {
|
||||
emit('+', line);
|
||||
}
|
||||
for line in &a[a.len() - suffix..a_end] {
|
||||
emit(' ', line);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
pub struct Arguments {
|
||||
pub entry: PathBuf,
|
||||
pub operation: Operation,
|
||||
pub write: bool,
|
||||
pub json: bool,
|
||||
}
|
||||
|
||||
pub const HELP: &str = "arcadia check ROOT [--json]\narcadia lint ROOT [--json]\narcadia stabilize ROOT [--write] [--json]\narcadia rename-alias ROOT OLD NEW [--write] [--json]\n\nROOT is an explicit .arcadia entry file; includes resolve relative to it.\nMutation commands preview by default. --write applies this invocation's candidate.\nMissing UUIDs in a preview are provisional until written. No canonical formatting.\nExit: 0 valid/success (engineering warnings allowed), 1 invalid source or semantic\ncommand rejection, 2 usage, 3 disk/path/conflict/pending-journal/output failure.\nNo implicit journal recovery. Limits: 64 MiB, 256 files, include depth32/edges1024,\n100000 graph objects, 250000 records, 500000 ordered references.\n";
|
||||
|
||||
pub fn arguments(args: &[std::ffi::OsString]) -> Result<Arguments, String> {
|
||||
let mut positional = Vec::new();
|
||||
let (mut write, mut json) = (false, false);
|
||||
for arg in args {
|
||||
if arg == "--write" && !write {
|
||||
write = true;
|
||||
} else if arg == "--json" && !json {
|
||||
json = true;
|
||||
} else if arg.to_string_lossy().starts_with('-') {
|
||||
return Err(format!(
|
||||
"Unknown or repeated option: {}",
|
||||
arg.to_string_lossy()
|
||||
));
|
||||
} else {
|
||||
positional.push(arg);
|
||||
}
|
||||
}
|
||||
let command = positional.first().and_then(|s| s.to_str()).unwrap_or("");
|
||||
let operation = match (command, positional.len()) {
|
||||
("check" | "lint", 2) if !write => Operation::Check,
|
||||
("stabilize", 2) => Operation::Stabilize,
|
||||
("rename-alias", 4) => Operation::RenameAlias {
|
||||
old: positional[2].to_str().ok_or("Alias must be UTF-8")?.into(),
|
||||
new: positional[3].to_str().ok_or("Alias must be UTF-8")?.into(),
|
||||
},
|
||||
_ => return Err(HELP.into()),
|
||||
};
|
||||
Ok(Arguments {
|
||||
entry: PathBuf::from(positional[1]),
|
||||
operation,
|
||||
write,
|
||||
json,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
use oa_cli::{Report, arguments, prepare};
|
||||
use std::{
|
||||
io::{self, Write},
|
||||
path::Path,
|
||||
process::ExitCode,
|
||||
};
|
||||
|
||||
fn human_report(report: &Report, show_diff: bool) -> io::Result<()> {
|
||||
let mut err = io::stderr().lock();
|
||||
for d in &report.diagnostics {
|
||||
let at = d
|
||||
.location
|
||||
.as_ref()
|
||||
.map(|l| {
|
||||
format!(
|
||||
"{}:{}:{}",
|
||||
l.path,
|
||||
l.line.unwrap_or(0),
|
||||
l.column.unwrap_or(0)
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| report.entry.clone());
|
||||
writeln!(
|
||||
err,
|
||||
"{at}: {}[{}] ({}): {}",
|
||||
d.severity, d.code, d.coordinate_space, d.message
|
||||
)?;
|
||||
}
|
||||
writeln!(
|
||||
err,
|
||||
"{}: {} file changes; {} identities to stabilize",
|
||||
report.status,
|
||||
report.changes.len(),
|
||||
report.identities_to_stabilize
|
||||
)?;
|
||||
if let Some(migration) = &report.migration {
|
||||
writeln!(
|
||||
err,
|
||||
"Source migration: schema {} → {} (separate from identity stabilization)",
|
||||
migration.from_schema, migration.to_schema
|
||||
)?;
|
||||
}
|
||||
for note in &report.notes {
|
||||
writeln!(err, "{note}")?;
|
||||
}
|
||||
if show_diff {
|
||||
let mut out = io::stdout().lock();
|
||||
for change in &report.changes {
|
||||
write!(out, "{}", change.diff)?;
|
||||
}
|
||||
out.flush()?;
|
||||
}
|
||||
err.flush()
|
||||
}
|
||||
|
||||
fn run() -> io::Result<u8> {
|
||||
let args: Vec<_> = std::env::args_os().skip(1).collect();
|
||||
if args.len() == 1 && (args[0] == "--help" || args[0] == "-h") {
|
||||
io::stdout().write_all(oa_cli::HELP.as_bytes())?;
|
||||
return Ok(0);
|
||||
}
|
||||
if args.len() == 1 && args[0] == "--version" {
|
||||
writeln!(io::stdout(), "arcadia {}", env!("CARGO_PKG_VERSION"))?;
|
||||
return Ok(0);
|
||||
}
|
||||
let json = args.iter().any(|a| a == "--json");
|
||||
let mut displayed = false;
|
||||
let report = match arguments(&args) {
|
||||
Err(error) => Report::failure(Path::new(""), "usage", 2, "usage", error),
|
||||
Ok(args) => match prepare(&args.entry, args.operation) {
|
||||
Err(report) => *report,
|
||||
Ok(prepared) => {
|
||||
if args.write {
|
||||
if !args.json {
|
||||
human_report(prepared.report(), true)?;
|
||||
displayed = true;
|
||||
}
|
||||
prepared.write()
|
||||
} else {
|
||||
prepared.into_report()
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
if json {
|
||||
serde_json::to_writer_pretty(io::stdout().lock(), &report)?;
|
||||
writeln!(io::stdout())?;
|
||||
} else {
|
||||
human_report(&report, !displayed)?;
|
||||
}
|
||||
Ok(report.exit_code)
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
match run() {
|
||||
Ok(code) => ExitCode::from(code),
|
||||
Err(error) => {
|
||||
let _ = writeln!(io::stderr(), "Output failed: {error}");
|
||||
ExitCode::from(3)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,425 @@
|
||||
use oa_cli::{LIMITS, Operation, Prepared, prepare};
|
||||
use oa_core::{Id, Project, source_files, source_project};
|
||||
use serde_json::Value;
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
struct Fixture(PathBuf);
|
||||
impl Fixture {
|
||||
fn new() -> Self {
|
||||
let path = std::env::temp_dir().join(format!("arcadia-cli-test-{}", Id::new_v4()));
|
||||
fs::create_dir(&path).unwrap();
|
||||
Self(path)
|
||||
}
|
||||
fn put(&self, name: &str, text: &str) {
|
||||
let path = self.0.join(name);
|
||||
fs::create_dir_all(path.parent().unwrap()).unwrap();
|
||||
fs::write(path, text).unwrap();
|
||||
}
|
||||
fn entry(&self) -> PathBuf {
|
||||
self.0.join("project.arcadia")
|
||||
}
|
||||
fn read(&self, name: &str) -> String {
|
||||
fs::read_to_string(self.0.join(name)).unwrap()
|
||||
}
|
||||
fn prepare(&self, op: Operation) -> Prepared {
|
||||
prepare(&self.entry(), op).unwrap_or_else(|report| panic!("{report:#?}"))
|
||||
}
|
||||
fn model(&self) -> Project {
|
||||
let disk = source_files::load_project(&self.entry(), LIMITS).unwrap();
|
||||
source_project::compile_bundle(&disk.reachable_bundle(), LIMITS)
|
||||
.unwrap()
|
||||
.decoded
|
||||
.project
|
||||
}
|
||||
fn cli(&self, args: &[&str]) -> (i32, Value) {
|
||||
let mut command = Command::new(env!("CARGO_BIN_EXE_arcadia"));
|
||||
command
|
||||
.arg(args[0])
|
||||
.arg(self.entry())
|
||||
.args(&args[1..])
|
||||
.arg("--json");
|
||||
let output = command.output().unwrap();
|
||||
assert!(
|
||||
output.stderr.is_empty(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
(
|
||||
output.status.code().unwrap(),
|
||||
serde_json::from_slice(&output.stdout).unwrap(),
|
||||
)
|
||||
}
|
||||
}
|
||||
impl Drop for Fixture {
|
||||
fn drop(&mut self) {
|
||||
let _ = fs::remove_dir_all(&self.0);
|
||||
}
|
||||
}
|
||||
|
||||
const SIMPLE: &str = "// 雨: authored defaults stay omitted\r\narcadia 7;\r\nproject rescue \"Rescue\" {\r\n oa_entity team \"Team\" {}\r\n}\r\n";
|
||||
|
||||
#[test]
|
||||
fn complete_alarm_example_preview_then_write_and_fixed_point() {
|
||||
let fixture = Fixture::new();
|
||||
let source = include_str!(
|
||||
"../../../harness/experiments/textual-authoring-20260911/alarm-response.arcadia"
|
||||
);
|
||||
fixture.put("project.arcadia", source);
|
||||
let (code, check) = fixture.cli(&["check"]);
|
||||
assert_eq!(code, 0);
|
||||
assert_eq!(check["diagnostics"], serde_json::json!([]));
|
||||
let (code, preview) = fixture.cli(&["stabilize"]);
|
||||
assert_eq!(code, 0);
|
||||
assert_eq!(preview["identities_to_stabilize"], 40);
|
||||
assert_eq!(preview["status"], "preview");
|
||||
assert_eq!(fixture.read("project.arcadia"), source);
|
||||
assert!(
|
||||
preview["changes"][0]["diff"]
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.contains("+project alarm_response")
|
||||
);
|
||||
let (code, written) = fixture.cli(&["stabilize", "--write"]);
|
||||
assert_eq!(code, 0);
|
||||
let saved = fixture.read("project.arcadia");
|
||||
assert_eq!(written["changes"][0]["after"], saved);
|
||||
let project = fixture.model();
|
||||
assert_eq!(project.identities().len(), 40);
|
||||
let mut stripped = saved.clone();
|
||||
for (id, _) in project.identities() {
|
||||
stripped = stripped.replace(&format!(" id \"{id}\""), "");
|
||||
}
|
||||
assert_eq!(stripped, source, "only identity clauses may be inserted");
|
||||
let (code, again) = fixture.cli(&["stabilize", "--write"]);
|
||||
assert_eq!(code, 0);
|
||||
assert_eq!(again["changes"], serde_json::json!([]));
|
||||
assert_eq!(again["status"], "unchanged");
|
||||
assert_eq!(fixture.model(), project);
|
||||
assert_eq!(fixture.read("project.arcadia"), saved);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn captured_candidate_exact_write_preserves_crlf_comments_and_noop_metadata() {
|
||||
let fixture = Fixture::new();
|
||||
fixture.put("project.arcadia", SIMPLE);
|
||||
let candidate = fixture.prepare(Operation::Stabilize);
|
||||
let expected = candidate.report().changes[0].after.clone();
|
||||
assert_eq!(candidate.report().identities_to_stabilize, 2);
|
||||
assert_eq!(fixture.read("project.arcadia"), SIMPLE);
|
||||
assert_eq!(candidate.write().exit_code, 0);
|
||||
assert_eq!(fixture.read("project.arcadia"), expected);
|
||||
assert!(expected.contains("// 雨: authored defaults stay omitted\r\n"));
|
||||
assert!(!expected.contains("description"));
|
||||
let metadata = fs::metadata(fixture.entry()).unwrap().modified().unwrap();
|
||||
let report = fixture.prepare(Operation::Stabilize).write();
|
||||
assert_eq!(report.status, "unchanged");
|
||||
assert!(!report.written);
|
||||
assert_eq!(
|
||||
fs::metadata(fixture.entry()).unwrap().modified().unwrap(),
|
||||
metadata
|
||||
);
|
||||
assert_eq!(fs::read_dir(&fixture.0).unwrap().count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias_rename_across_modules_changes_only_live_tokens_and_retains_all_identity() {
|
||||
let fixture = Fixture::new();
|
||||
fixture.put("project.arcadia", "arcadia 7; project rescue \"Rescue\" { include \"participants.arcadia\"; include \"work.arcadia\"; }\n");
|
||||
fixture.put(
|
||||
"participants.arcadia",
|
||||
"// @team is quoted documentation\noa_entity team \"Team @team\" {}\n",
|
||||
);
|
||||
fixture.put("work.arcadia", "oa_activity assess \"Assess\" {}\noa_activity report \"Report\" {}\nallocation assigned \"Assigned\" { source @team; target @assess; }\ninteraction transfer \"Transfer\" { source @assess; target @report; }\n");
|
||||
assert_eq!(fixture.prepare(Operation::Stabilize).write().exit_code, 0);
|
||||
let before = fixture.model();
|
||||
let original: BTreeMap<_, _> = ["project.arcadia", "participants.arcadia", "work.arcadia"]
|
||||
.into_iter()
|
||||
.map(|p| (p, fixture.read(p)))
|
||||
.collect();
|
||||
let team = before
|
||||
.identities()
|
||||
.into_iter()
|
||||
.find(|(_, alias)| *alias == "team")
|
||||
.unwrap()
|
||||
.0;
|
||||
let candidate = fixture.prepare(Operation::RenameAlias {
|
||||
old: "team".into(),
|
||||
new: "rescue_team".into(),
|
||||
});
|
||||
assert_eq!(candidate.report().changes.len(), 2);
|
||||
assert_eq!(fixture.model(), before, "preview cannot mutate");
|
||||
assert_eq!(candidate.write().exit_code, 0);
|
||||
let mut expected = before;
|
||||
expected.elements.get_mut(&team).unwrap().alias = "rescue_team".into();
|
||||
assert_eq!(fixture.model(), expected);
|
||||
assert_eq!(fixture.read("project.arcadia"), original["project.arcadia"]);
|
||||
assert_eq!(
|
||||
fixture.read("participants.arcadia"),
|
||||
original["participants.arcadia"].replacen("oa_entity team ", "oa_entity rescue_team ", 1)
|
||||
);
|
||||
assert_eq!(
|
||||
fixture.read("work.arcadia"),
|
||||
original["work.arcadia"].replace("source @team;", "source @rescue_team;")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_unicode_raw_edit_is_located_and_never_stabilized_or_written() {
|
||||
let fixture = Fixture::new();
|
||||
let raw = "arcadia 7; project p \"雨\" {\n oa_activity a \"A\" {}\n allocation alloc \"Alloc\" { source @missing; target @a; }\n}\n";
|
||||
fixture.put("project.arcadia", raw);
|
||||
for command in [
|
||||
vec!["check"],
|
||||
vec!["stabilize", "--write"],
|
||||
vec!["rename-alias", "a", "b", "--write"],
|
||||
] {
|
||||
let (code, report) = fixture.cli(&command);
|
||||
assert_eq!(code, 1);
|
||||
assert_eq!(fixture.read("project.arcadia"), raw);
|
||||
let diagnostics = report["diagnostics"].as_array().unwrap();
|
||||
assert!(!diagnostics.is_empty());
|
||||
for d in diagnostics {
|
||||
assert_eq!(d["coordinate_space"], "input");
|
||||
let at = &d["location"];
|
||||
let start = at["start"].as_u64().unwrap() as usize;
|
||||
assert!(raw.is_char_boundary(start));
|
||||
assert_eq!(
|
||||
at["line"].as_u64().unwrap() as usize,
|
||||
raw[..start].bytes().filter(|b| *b == b'\n').count() + 1
|
||||
);
|
||||
let line = raw[..start]
|
||||
.rsplit_once('\n')
|
||||
.map_or(&raw[..start], |(_, tail)| tail);
|
||||
assert_eq!(
|
||||
at["column"].as_u64().unwrap() as usize,
|
||||
line.chars().count() + 1
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_id_warning_uses_raw_location_and_never_exposes_ephemeral_lint_id() {
|
||||
let fixture = Fixture::new();
|
||||
let raw = "arcadia 7;\nproject p \"雨\" {\n oa_activity work \"Work\" {}\n}\n";
|
||||
fixture.put("project.arcadia", raw);
|
||||
let (code, result) = fixture.cli(&["lint"]);
|
||||
assert_eq!(code, 0, "engineering warnings do not reject admission");
|
||||
let warning = result["diagnostics"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.find(|d| d["code"] == "work_without_behavior_context")
|
||||
.unwrap();
|
||||
assert_eq!(warning["severity"], "warning");
|
||||
assert_eq!(
|
||||
warning["location"]["start"],
|
||||
raw.find("oa_activity").unwrap()
|
||||
);
|
||||
assert_eq!(warning["location"]["line"], 3);
|
||||
assert_eq!(warning["location"]["column"], 5);
|
||||
assert!(warning["object_id"].is_null());
|
||||
assert_eq!(fixture.read("project.arcadia"), raw);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn captured_dependency_change_refuses_all_writes_even_on_noop() {
|
||||
let fixture = Fixture::new();
|
||||
fixture.put("project.arcadia", SIMPLE);
|
||||
let pending = fixture.prepare(Operation::Stabilize);
|
||||
let external = format!("{SIMPLE}// editor changed this\n");
|
||||
fixture.put("project.arcadia", &external);
|
||||
let result = pending.write();
|
||||
assert_eq!(result.exit_code, 3);
|
||||
assert_eq!(result.diagnostics.last().unwrap().code, "external_change");
|
||||
assert_eq!(fixture.read("project.arcadia"), external);
|
||||
assert_eq!(fixture.prepare(Operation::Stabilize).write().exit_code, 0);
|
||||
let noop = fixture.prepare(Operation::Stabilize);
|
||||
assert!(noop.report().changes.is_empty());
|
||||
let latest = format!("{}// another edit\n", fixture.read("project.arcadia"));
|
||||
fixture.put("project.arcadia", &latest);
|
||||
assert_eq!(noop.write().exit_code, 3);
|
||||
assert_eq!(fixture.read("project.arcadia"), latest);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pending_journal_refuses_read_preview_and_write_without_recovery() {
|
||||
let fixture = Fixture::new();
|
||||
fixture.put("project.arcadia", SIMPLE);
|
||||
assert_eq!(fixture.prepare(Operation::Stabilize).write().exit_code, 0);
|
||||
let noop = fixture.prepare(Operation::Stabilize);
|
||||
let source = fixture.read("project.arcadia");
|
||||
// An opaque incomplete journal must never be interpreted/recovered by CLI.
|
||||
let journal = fixture.0.join(".arcadia-save-project.arcadia.journal");
|
||||
fs::create_dir(&journal).unwrap();
|
||||
fs::write(journal.join("sentinel"), b"retain").unwrap();
|
||||
for op in [Operation::Check, Operation::Stabilize] {
|
||||
let report = prepare(&fixture.entry(), op).err().unwrap();
|
||||
assert_eq!(report.exit_code, 3);
|
||||
assert!(report.diagnostics.iter().any(|d| d.code == "pending_save"));
|
||||
}
|
||||
assert_eq!(
|
||||
noop.write().exit_code,
|
||||
3,
|
||||
"saver must recheck after preview"
|
||||
);
|
||||
assert_eq!(fixture.read("project.arcadia"), source);
|
||||
assert_eq!(fs::read(journal.join("sentinel")).unwrap(), b"retain");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejected_alias_and_same_alias_preserve_source() {
|
||||
let fixture = Fixture::new();
|
||||
fixture.put("project.arcadia", SIMPLE);
|
||||
assert_eq!(fixture.prepare(Operation::Stabilize).write().exit_code, 0);
|
||||
let source = fixture.read("project.arcadia");
|
||||
for (old, new) in [
|
||||
("team", "rescue"),
|
||||
("team", "invalid alias"),
|
||||
("absent", "valid"),
|
||||
] {
|
||||
let (code, result) = fixture.cli(&["rename-alias", old, new, "--write"]);
|
||||
assert_eq!(code, 1);
|
||||
assert_eq!(result["status"], "rejected");
|
||||
assert_eq!(fixture.read("project.arcadia"), source);
|
||||
}
|
||||
let (code, result) = fixture.cli(&["rename-alias", "team", "team", "--write"]);
|
||||
assert_eq!(code, 0);
|
||||
assert_eq!(result["status"], "unchanged");
|
||||
assert_eq!(fixture.read("project.arcadia"), source);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_shows_missing_identity_and_separate_source_migration() {
|
||||
let fixture = Fixture::new();
|
||||
let old = "// retain\narcadia 5; project p \"P\" { oa_entity team \"Team\" {} }\n";
|
||||
fixture.put("project.arcadia", old);
|
||||
let candidate = fixture.prepare(Operation::RenameAlias {
|
||||
old: "team".into(),
|
||||
new: "crew".into(),
|
||||
});
|
||||
let report = candidate.report();
|
||||
assert_eq!(report.identities_to_stabilize, 2);
|
||||
assert_eq!(report.migration.as_ref().unwrap().from_schema, 5);
|
||||
assert_eq!(report.migration.as_ref().unwrap().to_schema, 7);
|
||||
assert!(report.changes[0].diff.contains("-arcadia 5;"));
|
||||
assert!(report.changes[0].diff.contains("+arcadia 7;"));
|
||||
assert!(report.changes[0].after.contains("oa_entity crew"));
|
||||
assert_eq!(fixture.read("project.arcadia"), old);
|
||||
assert_eq!(candidate.write().exit_code, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn entry_is_explicit_and_machine_readable_usage_errors_do_not_write() {
|
||||
let fixture = Fixture::new();
|
||||
fixture.put("project.arcadia", SIMPLE);
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_arcadia"))
|
||||
.current_dir(&fixture.0)
|
||||
.args(["check", "--json"])
|
||||
.output()
|
||||
.unwrap();
|
||||
assert_eq!(output.status.code(), Some(2));
|
||||
let error: Value = serde_json::from_slice(&output.stdout).unwrap();
|
||||
assert_eq!(error["diagnostics"][0]["code"], "usage");
|
||||
assert_eq!(fixture.read("project.arcadia"), SIMPLE);
|
||||
let (code, _) = fixture.cli(&["check", "--write"]);
|
||||
assert_eq!(code, 2);
|
||||
assert!(Path::new(env!("CARGO_BIN_EXE_arcadia")).is_file());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loader_encoding_missing_dependency_and_invalid_old_source_keep_original_bytes() {
|
||||
let fixture = Fixture::new();
|
||||
let entry = "arcadia 7; project p \"P\" { include \"part.arcadia\"; }";
|
||||
fixture.put("project.arcadia", entry);
|
||||
let (code, report) = fixture.cli(&["stabilize", "--write"]);
|
||||
assert_eq!(code, 3, "a disk dependency could not be read");
|
||||
assert!(
|
||||
report["diagnostics"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|d| d["code"] == "missing_include")
|
||||
);
|
||||
fs::write(fixture.0.join("part.arcadia"), [0xff, 0xfe]).unwrap();
|
||||
let (code, report) = fixture.cli(&["stabilize", "--write"]);
|
||||
assert_eq!(code, 1, "invalid encoding is invalid raw source");
|
||||
assert!(
|
||||
report["diagnostics"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|d| d["code"] == "source_encoding")
|
||||
);
|
||||
assert_eq!(fixture.read("project.arcadia"), entry);
|
||||
assert_eq!(
|
||||
fs::read(fixture.0.join("part.arcadia")).unwrap(),
|
||||
[0xff, 0xfe]
|
||||
);
|
||||
for raw in [
|
||||
"arcadia 5; project p \"P\" { oa_entity a \"A\" { ??? } }",
|
||||
"arcadia 2; project p \"P\" {}",
|
||||
] {
|
||||
fixture.put("project.arcadia", raw);
|
||||
let (code, _) = fixture.cli(&["stabilize", "--write"]);
|
||||
assert_eq!(code, 1);
|
||||
assert_eq!(fixture.read("project.arcadia"), raw);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_element_alias_detached_file_and_unchanged_dependency_conflict() {
|
||||
let fixture = Fixture::new();
|
||||
fixture.put("project.arcadia", "arcadia 7; project p \"P\" { include \"part.arcadia\"; oa_activity a \"A\" {} allocation responsible \"Responsible\" { source @ // keep edge comment\nteam; target @a; } }");
|
||||
fixture.put("part.arcadia", "oa_entity team \"Team\" {}");
|
||||
fixture.put(
|
||||
"detached.arcadia",
|
||||
"invalid detached draft must stay exact 雨",
|
||||
);
|
||||
assert_eq!(fixture.prepare(Operation::Stabilize).write().exit_code, 0);
|
||||
let before = fixture.model();
|
||||
let relation = before
|
||||
.identities()
|
||||
.into_iter()
|
||||
.find(|(_, alias)| *alias == "responsible")
|
||||
.unwrap()
|
||||
.0;
|
||||
let (code, _) = fixture.cli(&["rename-alias", "responsible", "performs", "--write"]);
|
||||
assert_eq!(code, 0);
|
||||
let mut expected = before;
|
||||
expected.relations.get_mut(&relation).unwrap().alias = "performs".into();
|
||||
assert_eq!(fixture.model(), expected);
|
||||
assert!(
|
||||
fixture
|
||||
.read("project.arcadia")
|
||||
.contains("@ // keep edge comment\nteam")
|
||||
);
|
||||
let captured = fixture.prepare(Operation::RenameAlias {
|
||||
old: "p".into(),
|
||||
new: "rescue".into(),
|
||||
});
|
||||
assert!(
|
||||
captured
|
||||
.report()
|
||||
.changes
|
||||
.iter()
|
||||
.all(|c| c.path != "part.arcadia")
|
||||
);
|
||||
let root_before = fixture.read("project.arcadia");
|
||||
fixture.put("part.arcadia", "// external unchanged-dependency edit\n");
|
||||
assert_eq!(captured.write().exit_code, 3);
|
||||
assert_eq!(fixture.read("project.arcadia"), root_before);
|
||||
assert_eq!(
|
||||
fixture.read("part.arcadia"),
|
||||
"// external unchanged-dependency edit\n"
|
||||
);
|
||||
assert_eq!(
|
||||
fixture.read("detached.arcadia"),
|
||||
"invalid detached draft must stay exact 雨"
|
||||
);
|
||||
}
|
||||
+215
-27
@@ -13,6 +13,7 @@ mod transition_context_tests;
|
||||
mod view_copy_tests;
|
||||
use crate::forms::Draft;
|
||||
use crate::{
|
||||
behavior,
|
||||
document::Document,
|
||||
source_workspace::{self, LIMITS, Workspace},
|
||||
};
|
||||
@@ -849,14 +850,28 @@ impl App {
|
||||
}
|
||||
fn execute(&mut self, command: Command) -> bool {
|
||||
let command = crate::port_edit::repair_owner_change(self.project(), command);
|
||||
let created = self.behavior.creation.created_by(self.project(), &command);
|
||||
let scenario_selection = self
|
||||
.behavior
|
||||
.scenario
|
||||
.created_selection(&command)
|
||||
.or_else(|| self.behavior.topology.created_selection(&command));
|
||||
.created_selection(self.project(), &command)
|
||||
.or_else(|| {
|
||||
self.behavior
|
||||
.topology
|
||||
.created_selection(self.project(), &command)
|
||||
});
|
||||
match self.document.execute(command) {
|
||||
Ok(selected) => {
|
||||
if let Some(id) = scenario_selection.or(selected) {
|
||||
if let Some(created) = created {
|
||||
self.layer = created.layer;
|
||||
self.selected = created.behavior;
|
||||
self.selected_node = None;
|
||||
self.behavior.active_owner = Some(created.behavior);
|
||||
self.behavior.diagram = behavior::state_view::State::default();
|
||||
self.behavior.diagram.diagram = created.diagram;
|
||||
self.page = Page::Behavior;
|
||||
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
|
||||
} else if let Some(id) = scenario_selection.or(selected) {
|
||||
self.selected = id;
|
||||
}
|
||||
self.sync();
|
||||
@@ -1019,6 +1034,11 @@ impl App {
|
||||
iced::time::every(std::time::Duration::from_secs(10)).map(|_| Message::CheckpointTick),
|
||||
window::close_requests().map(|id| Message::Request(Pending::Close(id))),
|
||||
window::resize_events().map(|(_, size)| Message::WindowResized(size)),
|
||||
architecture_authoring::subscription(self),
|
||||
behavior::creation::subscription(
|
||||
self.behavior.creation.has_pending() || self.behavior.source_active(),
|
||||
)
|
||||
.map(|()| Message::Escape),
|
||||
iced::event::listen_with(|event, status, _| {
|
||||
let iced::Event::Keyboard(keyboard::Event::KeyPressed {
|
||||
key,
|
||||
@@ -1076,9 +1096,7 @@ impl App {
|
||||
// navigation and close retain raw input until explicit completion/cancel.
|
||||
if self.architecture.is_some() {
|
||||
if matches!(message, Message::Escape) {
|
||||
self.architecture = None;
|
||||
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
|
||||
return Task::none();
|
||||
return self.architecture_event(architecture_authoring::Event::Cancel);
|
||||
}
|
||||
let placing = self.architecture.as_ref().is_some_and(|s| s.saved.placing);
|
||||
if !matches!(
|
||||
@@ -1104,6 +1122,7 @@ impl App {
|
||||
Message::ScenarioCanvas(
|
||||
crate::behavior::scenario::CanvasEvent::Scrolled(..)
|
||||
| crate::behavior::scenario::CanvasEvent::Restored(..)
|
||||
| crate::behavior::scenario::CanvasEvent::Viewport(..)
|
||||
) | Message::TopologyCanvas(
|
||||
crate::behavior::topology::CanvasEvent::Scrolled(..)
|
||||
| crate::behavior::topology::CanvasEvent::Viewport(..)
|
||||
@@ -1295,6 +1314,11 @@ impl App {
|
||||
.topology
|
||||
.enter(context.document, context.owner);
|
||||
}
|
||||
if let Some(context) = self.scenario_context() {
|
||||
self.behavior
|
||||
.scenario
|
||||
.enter(context.document, context.scenario);
|
||||
}
|
||||
if self.connection.as_ref().is_some_and(|session| {
|
||||
Some(session.context) != self.route_context()
|
||||
|| self.page != Page::Diagram
|
||||
@@ -1365,6 +1389,17 @@ impl App {
|
||||
Message::ScenarioCanvas(event) => {
|
||||
use crate::behavior::scenario::{self, CanvasEvent};
|
||||
let context = event.context();
|
||||
if let CanvasEvent::Viewport(_, size) = event {
|
||||
if self.scenario_context().is_some_and(|current| {
|
||||
current.document == context.document
|
||||
&& current.scenario == context.scenario
|
||||
&& current.revision == context.revision
|
||||
}) && self.behavior.scenario.viewport(size)
|
||||
{
|
||||
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
|
||||
}
|
||||
return Task::none();
|
||||
}
|
||||
if let CanvasEvent::Restored(_, token, area, offset) = event {
|
||||
if self.scenario_context().is_some_and(|current| {
|
||||
current.document == context.document && current.scenario == context.scenario
|
||||
@@ -1378,7 +1413,7 @@ impl App {
|
||||
return Task::none();
|
||||
}
|
||||
if let CanvasEvent::Scrolled(_, area, offset) = event {
|
||||
// Inner and outer scroll notifications may come from the
|
||||
// Canvas and outline notifications may come from the
|
||||
// same frame. Accept both across the first epoch bump,
|
||||
// while still rejecting another owner/document/revision.
|
||||
if self.scenario_context().is_some_and(|current| {
|
||||
@@ -1395,6 +1430,19 @@ impl App {
|
||||
return Task::none();
|
||||
}
|
||||
match event {
|
||||
CanvasEvent::Focus(_) => {
|
||||
self.behavior.scenario.toggle_focus();
|
||||
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
|
||||
}
|
||||
CanvasEvent::Shelf(_, shelf) => {
|
||||
self.behavior.scenario.toggle_shelf(shelf);
|
||||
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
|
||||
}
|
||||
CanvasEvent::Zoom(_, scale) => {
|
||||
if self.behavior.scenario.zoom(scale) {
|
||||
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
|
||||
}
|
||||
}
|
||||
CanvasEvent::OpenUse(_, occurrence) => {
|
||||
if self.behavior.has_pending() || self.draft_dirty() || self.source_dirty()
|
||||
{
|
||||
@@ -1406,6 +1454,9 @@ impl App {
|
||||
self.behavior.scenario = state;
|
||||
match target {
|
||||
Ok(target) => {
|
||||
// A command can preselect this target while the
|
||||
// caller remains visible, bypassing select below.
|
||||
self.behavior.active_owner = Some(target);
|
||||
self.select(target);
|
||||
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
|
||||
}
|
||||
@@ -1424,12 +1475,23 @@ impl App {
|
||||
let caller = state.back(self.project(), context);
|
||||
self.behavior.scenario = state;
|
||||
if let Some(caller) = caller {
|
||||
// A command may already have selected this UUID while
|
||||
// leaving the referenced definition open. select's
|
||||
// same-UUID fast path must not keep that old owner.
|
||||
self.behavior.active_owner = Some(caller.owner);
|
||||
self.select(caller.occurrence);
|
||||
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
|
||||
if let Some(context) = self.scenario_context() {
|
||||
self.behavior.scenario.restore_caller(context, caller);
|
||||
}
|
||||
} else {
|
||||
// An edit can select a retargeted caller outside the
|
||||
// definition that remains visible after rejected Back.
|
||||
if crate::behavior::selected_behavior(self.project(), self.selected)
|
||||
!= Some(context.scenario)
|
||||
{
|
||||
self.select(context.scenario);
|
||||
}
|
||||
self.status =
|
||||
"The calling scenario reference is no longer available.".into();
|
||||
}
|
||||
@@ -1444,7 +1506,11 @@ impl App {
|
||||
}
|
||||
}
|
||||
CanvasEvent::Choose(_, tool) => {
|
||||
if self.behavior.has_pending() || !self.apply() {
|
||||
if self.behavior.has_pending() || self.draft_dirty() || self.source_dirty()
|
||||
{
|
||||
self.status =
|
||||
"Apply or cancel pending edits before choosing a scenario tool."
|
||||
.into();
|
||||
return Task::none();
|
||||
}
|
||||
self.behavior.scenario.choose_tool(context.scenario, tool);
|
||||
@@ -1459,6 +1525,37 @@ impl App {
|
||||
return Task::none();
|
||||
}
|
||||
match mutation {
|
||||
CanvasEvent::First(_, tool) => {
|
||||
let empty = match tool {
|
||||
scenario::Tool::Lifeline => !self
|
||||
.project()
|
||||
.children(context.scenario)
|
||||
.any(|e| e.kind() == Kind::Lifeline),
|
||||
scenario::Tool::Message | scenario::Tool::Action => {
|
||||
self.project()
|
||||
.children(context.scenario)
|
||||
.any(|e| e.kind() == Kind::Lifeline)
|
||||
&& !self
|
||||
.project()
|
||||
.children(context.scenario)
|
||||
.any(|e| e.kind() == Kind::Occurrence)
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
if !empty {
|
||||
self.status="This scenario already has content. Use its current authoring tools.".into();
|
||||
return Task::none();
|
||||
}
|
||||
let mut state = std::mem::take(&mut self.behavior.scenario);
|
||||
let result = state.update(
|
||||
self.project(),
|
||||
scenario::Event::Start(context.scenario, tool),
|
||||
);
|
||||
self.behavior.scenario = state;
|
||||
if let Err(error) = result {
|
||||
self.status = error;
|
||||
}
|
||||
}
|
||||
CanvasEvent::BeginOccurrence {
|
||||
tool,
|
||||
slot,
|
||||
@@ -1694,6 +1791,18 @@ impl App {
|
||||
return self.update(Message::Add(child));
|
||||
}
|
||||
Message::Escape => {
|
||||
if self.behavior.source_back() {
|
||||
self.status =
|
||||
"Returned to the unchanged parent form; no source was created.".into();
|
||||
return Task::none();
|
||||
}
|
||||
if self.behavior.source_active() {
|
||||
self.behavior.acknowledge_commit();
|
||||
self.status = "Behavior form cancelled; no staged source was created.".into();
|
||||
} else if self.behavior.creation.has_pending() {
|
||||
self.behavior.creation.acknowledge_commit();
|
||||
self.status = "Behavior creation cancelled".into();
|
||||
}
|
||||
if self.connection.take().is_some() {
|
||||
self.status = "Drawing cancelled · Select / move is active".into();
|
||||
}
|
||||
@@ -2237,9 +2346,33 @@ impl App {
|
||||
}
|
||||
Message::InspectorTab(tab) => self.inspector_tab = tab,
|
||||
Message::Behavior(event) => {
|
||||
if event.starts_form() && !self.behavior.has_pending() && !self.apply() {
|
||||
if matches!(&event, behavior::Event::Creation(behavior::creation::Event::Open(layer)) if *layer != self.layer)
|
||||
{
|
||||
self.status =
|
||||
"The active layer changed. Choose New in the current layer.".into();
|
||||
return Task::none();
|
||||
}
|
||||
let creating = matches!(
|
||||
&event,
|
||||
behavior::Event::Creation(behavior::creation::Event::Open(..))
|
||||
);
|
||||
let focus_name = matches!(
|
||||
&event,
|
||||
behavior::Event::Creation(behavior::creation::Event::Choose(..))
|
||||
);
|
||||
if creating
|
||||
&& (self.behavior.has_pending() || self.source_dirty() || self.draft_dirty())
|
||||
{
|
||||
self.status = "Apply or cancel the current draft before starting a new behavior. Your inputs are retained.".into();
|
||||
return Task::none();
|
||||
}
|
||||
if (event.starts_form() || (event.is_source() && !event.is_source_back()))
|
||||
&& (self.source_dirty() || self.draft_dirty())
|
||||
{
|
||||
self.status="Apply or cancel the other editors before starting this form. Your inputs are retained.".into();
|
||||
return Task::none();
|
||||
}
|
||||
let source_was_open = self.behavior.source_open();
|
||||
if event.is_submit() && (self.source_dirty() || self.draft_dirty()) {
|
||||
self.status = "Revert the other drafts before applying the behavior form. Your inputs are retained.".into();
|
||||
return Task::none();
|
||||
@@ -2256,7 +2389,16 @@ impl App {
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None) => {}
|
||||
Ok(None) => {
|
||||
if !source_was_open && self.behavior.source_open() {
|
||||
return iced::widget::operation::focus(
|
||||
behavior::source_draft::NAME_INPUT,
|
||||
);
|
||||
}
|
||||
if focus_name {
|
||||
return iced::widget::operation::focus(behavior::creation::NAME_INPUT);
|
||||
}
|
||||
}
|
||||
Err(error) => self.status = error,
|
||||
}
|
||||
}
|
||||
@@ -2448,13 +2590,19 @@ impl App {
|
||||
Message::Undo | Message::Redo => {
|
||||
let redo = matches!(message, Message::Redo);
|
||||
if self.apply() {
|
||||
if redo {
|
||||
self.document.redo();
|
||||
let changed = if redo {
|
||||
self.document.redo()
|
||||
} else {
|
||||
self.document.undo();
|
||||
self.document.undo()
|
||||
};
|
||||
if changed {
|
||||
self.sync();
|
||||
}
|
||||
self.sync();
|
||||
self.status = if redo {
|
||||
self.status = if !changed && redo {
|
||||
"Nothing to redo"
|
||||
} else if !changed {
|
||||
"Nothing to undo"
|
||||
} else if redo {
|
||||
"Model change redone"
|
||||
} else {
|
||||
"Model change undone"
|
||||
@@ -3142,6 +3290,52 @@ impl App {
|
||||
id,
|
||||
));
|
||||
}
|
||||
// Refining a leaf moves its explicit responsibilities to the new leaf.
|
||||
// Later children inherit only the existing unambiguous performer summary.
|
||||
if self.create_kind.is_work()
|
||||
&& let Some(owner) = parent
|
||||
&& self
|
||||
.project()
|
||||
.elements
|
||||
.get(&owner)
|
||||
.is_some_and(|e| e.layer() == self.layer && e.kind() == self.create_kind)
|
||||
{
|
||||
let performer = oa_core::validation::allocation_summary(self.project(), owner);
|
||||
let leaf = !self
|
||||
.project()
|
||||
.children(owner)
|
||||
.any(|e| e.kind() == self.create_kind);
|
||||
let responsibilities: Vec<_> = self
|
||||
.project()
|
||||
.relations
|
||||
.values()
|
||||
.filter(|r| {
|
||||
r.target() == owner
|
||||
&& matches!(
|
||||
r.kind(),
|
||||
RelationKind::Allocation | RelationKind::RoleAssignment
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
if leaf && !responsibilities.is_empty() {
|
||||
for original in responsibilities {
|
||||
let mut responsibility = original.clone();
|
||||
match &mut responsibility.body {
|
||||
RelationBody::Allocation { target, .. }
|
||||
| RelationBody::RoleAssignment { target, .. } => *target = id,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
commands.push(Command::EditRelation(original.id, responsibility));
|
||||
}
|
||||
} else if let Some(performer) = performer {
|
||||
commands.push(Command::Connect(
|
||||
RelationKind::Allocation,
|
||||
"Performs".into(),
|
||||
performer,
|
||||
id,
|
||||
));
|
||||
}
|
||||
}
|
||||
if self.create_kind == Kind::Occurrence
|
||||
&& let Some(owner) = parent.and_then(|id| self.project().elements.get(&id))
|
||||
&& let Some(steps) = owner.steps()
|
||||
@@ -3159,19 +3353,13 @@ impl App {
|
||||
.get(&diagram)
|
||||
.is_none_or(|d| d.projection.includes(self.create_kind))
|
||||
{
|
||||
let mut p = self.project().clone();
|
||||
for command in &commands {
|
||||
if let Command::CreateDiagram(d) = command {
|
||||
p.diagrams.insert(d.id, d.clone());
|
||||
let mut p = match execute_batch(self.project(), &commands) {
|
||||
Ok(candidate) => candidate,
|
||||
Err(error) => {
|
||||
self.status = error.to_string();
|
||||
return;
|
||||
}
|
||||
}
|
||||
p.elements.insert(
|
||||
id,
|
||||
match &commands[0] {
|
||||
Command::CreateElement(e) => e.clone(),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
);
|
||||
};
|
||||
let owner = if allocated {
|
||||
Some(self.selected)
|
||||
} else {
|
||||
|
||||
@@ -3,6 +3,8 @@ use super::*;
|
||||
use iced::widget::{button, column, pick_list, row, scrollable, text, text_input};
|
||||
use serde::{Deserialize, Serialize};
|
||||
mod palette;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
pub use palette::view as palette;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -21,9 +23,35 @@ pub enum Event {
|
||||
Filter(Filter),
|
||||
ShowPlaced(bool),
|
||||
Page(usize),
|
||||
Edit,
|
||||
CapturedEscape,
|
||||
Cancel,
|
||||
}
|
||||
|
||||
pub fn subscription(app: &App) -> Subscription<Message> {
|
||||
if app.architecture.as_ref().is_some_and(State::inline) {
|
||||
iced::event::listen_with(captured_escape)
|
||||
} else {
|
||||
Subscription::none()
|
||||
}
|
||||
}
|
||||
|
||||
fn captured_escape(
|
||||
event: iced::Event,
|
||||
status: iced::event::Status,
|
||||
_: iced::window::Id,
|
||||
) -> Option<Message> {
|
||||
// A focused Iced text input captures Escape while dropping its focus. Only
|
||||
// inline architecture naming opts into also cancelling that same keypress.
|
||||
(status == iced::event::Status::Captured
|
||||
&& matches!(
|
||||
event,
|
||||
iced::Event::Keyboard(iced::keyboard::Event::KeyPressed {
|
||||
key: iced::keyboard::Key::Named(iced::keyboard::key::Named::Escape),
|
||||
..
|
||||
})
|
||||
))
|
||||
.then_some(Message::Architecture(Event::CapturedEscape))
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub enum Filter {
|
||||
@@ -336,9 +364,15 @@ impl App {
|
||||
}
|
||||
return iced::widget::operation::focus("architecture-name");
|
||||
}
|
||||
Event::CapturedEscape => {
|
||||
if self.architecture.as_ref().is_some_and(State::inline) {
|
||||
return self.architecture_event(Event::Cancel);
|
||||
}
|
||||
}
|
||||
Event::Cancel => {
|
||||
self.architecture = None;
|
||||
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
|
||||
self.status = "Cancelled · Select / move".into();
|
||||
}
|
||||
Event::Name(value) => {
|
||||
if value.len() <= 16384
|
||||
@@ -375,13 +409,6 @@ impl App {
|
||||
s.saved.page = page.min(1_000_000);
|
||||
}
|
||||
}
|
||||
Event::Edit => {
|
||||
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
|
||||
if let Some(s) = &mut self.architecture {
|
||||
s.saved.placing = false;
|
||||
}
|
||||
return iced::widget::operation::focus("architecture-name");
|
||||
}
|
||||
Event::Pick(id) => {
|
||||
if !candidates(self)
|
||||
.iter()
|
||||
@@ -560,9 +587,10 @@ pub fn inline_editor(app: &App) -> Option<Element<'_, Message>> {
|
||||
row![
|
||||
iced::widget::Space::new().width(x),
|
||||
iced::widget::opaque(
|
||||
iced::widget::container(name_editor(app))
|
||||
iced::widget::container(scrollable(name_editor(app)))
|
||||
.padding(10)
|
||||
.width(width)
|
||||
.max_height((size.height - y - 8.).max(0.))
|
||||
.style(crate::style::card)
|
||||
)
|
||||
]
|
||||
|
||||
@@ -4,7 +4,7 @@ use iced::{
|
||||
Point, Rectangle, Size, Theme, mouse,
|
||||
widget::{
|
||||
canvas::{self, Frame, Geometry, Path, Stroke},
|
||||
column, container, tooltip,
|
||||
container, tooltip,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -165,7 +165,7 @@ fn tool<'a>(
|
||||
.into()
|
||||
}
|
||||
pub fn view(app: &App) -> Element<'_, Message> {
|
||||
let mut content = column![tool(
|
||||
let mut content = iced::widget::column![tool(
|
||||
app,
|
||||
Icon::Collapse(app.architecture_palette_expanded),
|
||||
if app.architecture_palette_expanded {
|
||||
|
||||
@@ -0,0 +1,378 @@
|
||||
use super::*;
|
||||
|
||||
fn setup(layer: Layer) -> App {
|
||||
let mut app = App::with_project(Project::empty());
|
||||
app.layer = layer;
|
||||
app.active_diagram = None;
|
||||
let _ = app.update(Message::AddDiagram);
|
||||
app.zoom = 0.75;
|
||||
app.pan = iced::Vector::new(37., -19.);
|
||||
app
|
||||
}
|
||||
fn send(app: &mut App, event: Event) {
|
||||
let _ = app.update(Message::Architecture(event));
|
||||
}
|
||||
fn create(app: &mut App, kind: Kind, name: &str) -> Id {
|
||||
send(app, Event::Start(kind, Some(Position { x: 120., y: 160. })));
|
||||
send(app, Event::Name(name.into()));
|
||||
send(app, Event::Submit);
|
||||
assert!(app.architecture.is_none(), "{}", app.status);
|
||||
app.selected
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn architecture_allocated_work_is_root_owned_and_one_exact_history_change() {
|
||||
for (layer, participant, work) in [
|
||||
(Layer::Oa, Kind::Entity, Kind::Activity),
|
||||
(Layer::Sa, Kind::System, Kind::Function),
|
||||
] {
|
||||
let mut app = setup(layer);
|
||||
let performer = create(&mut app, participant, "Control team");
|
||||
let before = app.document.bundle().clone();
|
||||
let camera = (app.zoom, app.pan);
|
||||
let point = Position { x: 151., y: 221. };
|
||||
send(&mut app, Event::Allocate(work, performer, Some(point)));
|
||||
send(&mut app, Event::Name(" Report 雨 ".into()));
|
||||
assert_eq!(app.document.bundle(), &before);
|
||||
send(&mut app, Event::Submit);
|
||||
assert!(app.architecture.is_none(), "{}", app.status);
|
||||
let created = app.selected;
|
||||
let element = &app.project().elements[&created];
|
||||
assert_eq!(element.kind(), work);
|
||||
assert_eq!(element.name, " Report 雨 ");
|
||||
assert_eq!(element.parent, None);
|
||||
assert!(app.project().relations.values().any(|r| {
|
||||
r.kind() == RelationKind::Allocation && r.source() == performer && r.target() == created
|
||||
}));
|
||||
let d = &app.project().diagrams[&app.active_diagram.unwrap()];
|
||||
assert_eq!(d.nodes[&app.selected_node.unwrap()].position, point);
|
||||
assert_eq!((app.zoom, app.pan), camera);
|
||||
let accepted = app.document.bundle().clone();
|
||||
let _ = app.update(Message::Undo);
|
||||
assert_eq!(app.document.bundle(), &before);
|
||||
let _ = app.update(Message::Redo);
|
||||
assert_eq!(app.document.bundle(), &accepted);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn architecture_f2_routes_selected_node_to_inline_rename_and_exact_undo() {
|
||||
use iced::widget::canvas::{Event as CanvasEvent, Program};
|
||||
use iced::{keyboard, mouse};
|
||||
let mut app = setup(Layer::Oa);
|
||||
let element = create(&mut app, Kind::Entity, "Original team");
|
||||
let before = app.document.bundle().clone();
|
||||
let diagrams = app.project().diagrams.clone();
|
||||
let camera = (app.zoom, app.pan);
|
||||
let bounds = iced::Rectangle::new(iced::Point::ORIGIN, iced::Size::new(600., 380.));
|
||||
let canvas = crate::diagram::DiagramCanvas {
|
||||
project: app.project(),
|
||||
view_index: None,
|
||||
diagram: app.active_diagram.unwrap(),
|
||||
selected: element,
|
||||
zoom: app.zoom,
|
||||
pan: app.pan,
|
||||
context: app.route_context().unwrap(),
|
||||
selected_bend: None,
|
||||
editable: true,
|
||||
placing: false,
|
||||
connection: None,
|
||||
};
|
||||
let mut state = crate::diagram::State::default();
|
||||
let key = CanvasEvent::Keyboard(keyboard::Event::KeyPressed {
|
||||
key: keyboard::Key::Named(keyboard::key::Named::F2),
|
||||
modified_key: keyboard::Key::Named(keyboard::key::Named::F2),
|
||||
physical_key: keyboard::key::Physical::Code(keyboard::key::Code::F2),
|
||||
location: keyboard::Location::Standard,
|
||||
modifiers: keyboard::Modifiers::empty(),
|
||||
text: None,
|
||||
repeat: false,
|
||||
});
|
||||
assert!(
|
||||
canvas
|
||||
.update(&mut state, &key, bounds, mouse::Cursor::Unavailable)
|
||||
.is_none()
|
||||
);
|
||||
let _ = canvas.update(
|
||||
&mut state,
|
||||
&CanvasEvent::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)),
|
||||
bounds,
|
||||
mouse::Cursor::Available(iced::Point::new(150., 120.)),
|
||||
);
|
||||
let action = canvas
|
||||
.update(&mut state, &key, bounds, mouse::Cursor::Unavailable)
|
||||
.unwrap();
|
||||
let message = action.into_inner().0.unwrap();
|
||||
let _ = app.update(message);
|
||||
assert!(app.architecture.as_ref().unwrap().inline());
|
||||
assert!(matches!(&app.architecture.as_ref().unwrap().saved.intent,
|
||||
Intent::Rename { element: id, name } if *id == element && name == "Original team"));
|
||||
send(&mut app, Event::Name(" Renamed team 雨 ".into()));
|
||||
assert_eq!(app.document.bundle(), &before);
|
||||
send(&mut app, Event::Submit);
|
||||
assert!(app.architecture.is_none(), "{}", app.status);
|
||||
assert_eq!(app.project().elements[&element].name, " Renamed team 雨 ");
|
||||
assert_eq!(app.project().diagrams, diagrams);
|
||||
assert_eq!((app.zoom, app.pan), camera);
|
||||
let accepted = app.document.bundle().clone();
|
||||
let _ = app.update(Message::Undo);
|
||||
assert_eq!(app.document.bundle(), &before);
|
||||
let _ = app.update(Message::Redo);
|
||||
assert_eq!(app.document.bundle(), &accepted);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn architecture_blank_names_and_escape_never_accept_placeholder_or_rename() {
|
||||
let mut app = setup(Layer::Oa);
|
||||
let element = create(&mut app, Kind::Actor, "Dispatcher");
|
||||
let before = app.document.bundle().clone();
|
||||
let context = app.route_context().unwrap();
|
||||
for event in [
|
||||
Event::Start(Kind::Activity, Some(Position { x: 220., y: 330. })),
|
||||
Event::Rename(context, element, Position { x: 150., y: 200. }),
|
||||
] {
|
||||
// Every cancel changes the gesture context; use the current context for F2.
|
||||
let event = match event {
|
||||
Event::Rename(_, element, point) => {
|
||||
Event::Rename(app.route_context().unwrap(), element, point)
|
||||
}
|
||||
event => event,
|
||||
};
|
||||
send(&mut app, event);
|
||||
send(&mut app, Event::Name(" \t ".into()));
|
||||
send(&mut app, Event::Submit);
|
||||
assert!(app.architecture.as_ref().unwrap().inline());
|
||||
assert!(app.architecture.as_ref().unwrap().error.is_some());
|
||||
assert_eq!(app.document.bundle(), &before);
|
||||
let _ = app.update(Message::Escape);
|
||||
assert!(app.architecture.is_none());
|
||||
assert_eq!(app.document.bundle(), &before);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn architecture_invalid_click_cannot_poison_inline_or_checkpoint_geometry() {
|
||||
let mut app = setup(Layer::Oa);
|
||||
send(&mut app, Event::Start(Kind::Entity, None));
|
||||
let before = app.document.bundle().clone();
|
||||
let context = app.route_context().unwrap();
|
||||
for point in [
|
||||
Position { x: f32::NAN, y: 0. },
|
||||
Position {
|
||||
x: 0.,
|
||||
y: f32::INFINITY,
|
||||
},
|
||||
] {
|
||||
send(&mut app, Event::Click(context, point));
|
||||
let draft = app.architecture.as_ref().unwrap();
|
||||
assert!(draft.unnamed_tool());
|
||||
assert!(validate_saved(app.project(), &draft.saved).is_ok());
|
||||
assert_eq!(app.document.bundle(), &before);
|
||||
}
|
||||
send(&mut app, Event::Click(context, Position { x: 25., y: 30. }));
|
||||
assert!(app.architecture.as_ref().unwrap().inline());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn architecture_stale_submit_keeps_create_and_rename_visible_and_exact() {
|
||||
for rename in [false, true] {
|
||||
for stale_document in [false, true] {
|
||||
let mut app = setup(Layer::Oa);
|
||||
let element = create(&mut app, Kind::Entity, "Accepted team");
|
||||
let point = Position {
|
||||
x: 151.5,
|
||||
y: 227.25,
|
||||
};
|
||||
let event = if rename {
|
||||
Event::Rename(app.route_context().unwrap(), element, point)
|
||||
} else {
|
||||
Event::Start(Kind::Activity, Some(point))
|
||||
};
|
||||
send(&mut app, event);
|
||||
send(
|
||||
&mut app,
|
||||
Event::Name(" Retained engineering name 雨 ".into()),
|
||||
);
|
||||
// Change only the binding. The accepted model/source must not be
|
||||
// rewritten or a rejected draft silently rebound to the new context.
|
||||
if stale_document {
|
||||
app.architecture.as_mut().unwrap().document = Id::new_v4();
|
||||
} else {
|
||||
app.architecture.as_mut().unwrap().revision = app.document.revision() + 1;
|
||||
}
|
||||
let draft = app.architecture.as_ref().unwrap();
|
||||
let saved = serde_json::to_value(&draft.saved).unwrap();
|
||||
let binding = (draft.document, draft.revision);
|
||||
let model = app.project().clone();
|
||||
let source = app.document.bundle().clone();
|
||||
let revision = app.document.revision();
|
||||
let camera = (app.zoom, app.pan);
|
||||
|
||||
for _ in 0..2 {
|
||||
send(&mut app, Event::Submit);
|
||||
let draft = app.architecture.as_ref().unwrap();
|
||||
assert!(
|
||||
draft.inline(),
|
||||
"rename={rename}, stale_document={stale_document}"
|
||||
);
|
||||
assert!(inline_editor(&app).is_some());
|
||||
assert_eq!(draft.error.as_deref(), Some(app.status.as_str()));
|
||||
assert!(app.status.contains("cancel and restart"));
|
||||
assert_eq!(serde_json::to_value(&draft.saved).unwrap(), saved);
|
||||
assert_eq!((draft.document, draft.revision), binding);
|
||||
assert_eq!(app.project(), &model);
|
||||
assert_eq!(app.document.bundle(), &source);
|
||||
assert_eq!(app.document.revision(), revision);
|
||||
assert_eq!((app.zoom, app.pan), camera);
|
||||
}
|
||||
let _ = app.update(Message::Escape);
|
||||
assert!(app.architecture.is_none());
|
||||
assert_eq!(app.project(), &model);
|
||||
assert_eq!(app.document.bundle(), &source);
|
||||
let fresh = create(&mut app, Kind::Actor, "Fresh context");
|
||||
assert_eq!(app.project().elements[&fresh].name, "Fresh context");
|
||||
let _ = app.update(Message::Undo);
|
||||
assert_eq!(app.project(), &model);
|
||||
assert_eq!(app.document.bundle(), &source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn key_event(named: iced::keyboard::key::Named) -> iced::Event {
|
||||
use iced::keyboard;
|
||||
iced::Event::Keyboard(keyboard::Event::KeyPressed {
|
||||
key: keyboard::Key::Named(named),
|
||||
modified_key: keyboard::Key::Named(named),
|
||||
physical_key: keyboard::key::Physical::Code(keyboard::key::Code::Escape),
|
||||
location: keyboard::Location::Standard,
|
||||
modifiers: keyboard::Modifiers::empty(),
|
||||
text: None,
|
||||
repeat: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn focused_text_input_escape(name: &str) -> Message {
|
||||
use iced::advanced::{Layout, Shell, layout, widget};
|
||||
let mut field: iced::Element<'_, (), iced::Theme, ()> = text_input("Engineering name", name)
|
||||
.id("architecture-name")
|
||||
.on_input(|_| ())
|
||||
.into();
|
||||
let mut tree = widget::Tree::new(field.as_widget());
|
||||
let size = iced::Size::new(300., 40.);
|
||||
let node =
|
||||
field
|
||||
.as_widget_mut()
|
||||
.layout(&mut tree, &(), &layout::Limits::new(iced::Size::ZERO, size));
|
||||
tree.state
|
||||
.downcast_mut::<iced::widget::text_input::State<()>>()
|
||||
.focus();
|
||||
let escape = key_event(iced::keyboard::key::Named::Escape);
|
||||
let mut messages = Vec::new();
|
||||
let mut shell = Shell::new(&mut messages);
|
||||
field.as_widget_mut().update(
|
||||
&mut tree,
|
||||
&escape,
|
||||
Layout::new(&node),
|
||||
iced::mouse::Cursor::Unavailable,
|
||||
&(),
|
||||
&mut iced::advanced::clipboard::Null,
|
||||
&mut shell,
|
||||
&iced::Rectangle::new(iced::Point::ORIGIN, size),
|
||||
);
|
||||
assert!(
|
||||
shell.is_event_captured(),
|
||||
"Iced consumes focused-input Escape"
|
||||
);
|
||||
assert!(
|
||||
!tree
|
||||
.state
|
||||
.downcast_ref::<iced::widget::text_input::State<()>>()
|
||||
.is_focused()
|
||||
);
|
||||
captured_escape(
|
||||
escape,
|
||||
iced::event::Status::Captured,
|
||||
iced::window::Id::unique(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn architecture_first_focused_input_escape_cancels_create_and_rename() {
|
||||
for rename in [false, true] {
|
||||
let mut app = setup(Layer::Oa);
|
||||
let element = create(&mut app, Kind::Actor, "Accepted dispatcher");
|
||||
let model = app.project().clone();
|
||||
let source = app.document.bundle().clone();
|
||||
let revision = app.document.revision();
|
||||
let camera = (app.zoom, app.pan);
|
||||
let point = Position { x: 439., y: 328.2 };
|
||||
let event = if rename {
|
||||
Event::Rename(app.route_context().unwrap(), element, point)
|
||||
} else {
|
||||
Event::Start(Kind::Actor, Some(point))
|
||||
};
|
||||
send(&mut app, event);
|
||||
let name = " Unaccepted long engineering name 雨 ";
|
||||
send(&mut app, Event::Name(name.into()));
|
||||
assert!(app.architecture.as_ref().unwrap().inline());
|
||||
let message = focused_text_input_escape(name);
|
||||
let _ = app.update(message);
|
||||
assert!(app.architecture.is_none());
|
||||
assert!(inline_editor(&app).is_none());
|
||||
assert_eq!(app.status, "Cancelled · Select / move");
|
||||
assert_eq!(app.project(), &model);
|
||||
assert_eq!(app.document.bundle(), &source);
|
||||
assert_eq!(app.document.revision(), revision);
|
||||
assert_eq!((app.zoom, app.pan), camera);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn architecture_captured_escape_does_not_cancel_other_controls_or_placement() {
|
||||
let mut app = setup(Layer::Oa);
|
||||
for event in [
|
||||
None,
|
||||
Some(Event::Start(Kind::Entity, None)),
|
||||
Some(Event::Existing(None)),
|
||||
] {
|
||||
send(&mut app, Event::Cancel);
|
||||
if let Some(event) = event {
|
||||
send(&mut app, event);
|
||||
}
|
||||
let saved = app
|
||||
.architecture
|
||||
.as_ref()
|
||||
.map(|s| serde_json::to_value(&s.saved).unwrap());
|
||||
let before = app.document.bundle().clone();
|
||||
let status = app.status.clone();
|
||||
let epoch = app.canvas_epoch;
|
||||
let message = focused_text_input_escape("Other field or existing-element search");
|
||||
let _ = app.update(message);
|
||||
assert_eq!(
|
||||
app.architecture
|
||||
.as_ref()
|
||||
.map(|s| serde_json::to_value(&s.saved).unwrap()),
|
||||
saved
|
||||
);
|
||||
assert_eq!(app.document.bundle(), &before);
|
||||
assert_eq!(app.status, status);
|
||||
assert_eq!(app.canvas_epoch, epoch);
|
||||
}
|
||||
assert!(
|
||||
captured_escape(
|
||||
key_event(iced::keyboard::key::Named::Escape),
|
||||
iced::event::Status::Ignored,
|
||||
iced::window::Id::unique(),
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
assert!(
|
||||
captured_escape(
|
||||
key_event(iced::keyboard::key::Named::Enter),
|
||||
iced::event::Status::Captured,
|
||||
iced::window::Id::unique(),
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
@@ -173,7 +173,12 @@ impl App {
|
||||
self.behavior = restored.behavior;
|
||||
self.workflow = restored.workflow;
|
||||
let focus = self.behavior.pending_owner().unwrap_or(self.selected);
|
||||
if let Some(layer) = self.project().layer_of(focus) {
|
||||
if let Some(layer) = self
|
||||
.behavior
|
||||
.creation
|
||||
.pending_layer()
|
||||
.or_else(|| self.project().layer_of(focus))
|
||||
{
|
||||
self.layer = layer;
|
||||
self.explorer_index = crate::explorer::Index::new(self.project(), layer);
|
||||
self.active_diagram = self.diagram_for(layer, focus);
|
||||
@@ -202,7 +207,26 @@ impl App {
|
||||
} else {
|
||||
"Recovered an unsaved local copy. Choose Save as to save it; the original file and hosted model are unchanged.".into()
|
||||
};
|
||||
self.request_lint()
|
||||
let lint = self.request_lint();
|
||||
if self
|
||||
.architecture
|
||||
.as_ref()
|
||||
.is_some_and(architecture_authoring::State::inline)
|
||||
{
|
||||
Task::batch([lint, iced::widget::operation::focus("architecture-name")])
|
||||
} else if self.page == Page::Behavior && self.behavior.source_open() {
|
||||
Task::batch([
|
||||
lint,
|
||||
iced::widget::operation::focus(behavior::source_draft::NAME_INPUT),
|
||||
])
|
||||
} else if self.page == Page::Behavior && self.behavior.creation.created().is_some() {
|
||||
Task::batch([
|
||||
lint,
|
||||
iced::widget::operation::focus(behavior::creation::NAME_INPUT),
|
||||
])
|
||||
} else {
|
||||
lint
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,6 +355,177 @@ mod tests {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
async fn run_recovery_tasks(app: &mut App, task: Task<Message>) -> (usize, bool, bool) {
|
||||
use iced::advanced::{Layout, layout, widget};
|
||||
use iced::futures::StreamExt;
|
||||
// Mount real inputs, with focus initially in another control. This
|
||||
// verifies the returned operation targets the naming field by its ID.
|
||||
let mut fields: iced::Element<'_, (), iced::Theme, ()> = iced::widget::column![
|
||||
iced::widget::text_input("Engineering name", "Retained name")
|
||||
.id("architecture-name")
|
||||
.on_input(|_| ()),
|
||||
iced::widget::text_input("Other control", "Other value")
|
||||
.id("unrelated-field")
|
||||
.on_input(|_| ())
|
||||
]
|
||||
.into();
|
||||
let mut tree = widget::Tree::new(fields.as_widget());
|
||||
let node = fields.as_widget_mut().layout(
|
||||
&mut tree,
|
||||
&(),
|
||||
&layout::Limits::new(iced::Size::ZERO, iced::Size::new(320., 100.)),
|
||||
);
|
||||
tree.children[1]
|
||||
.state
|
||||
.downcast_mut::<iced::widget::text_input::State<()>>()
|
||||
.focus();
|
||||
let mut operations = 0;
|
||||
let mut lint_completions = 0;
|
||||
let mut stream = iced_runtime::task::into_stream(task).expect("Recovery must still lint");
|
||||
while let Some(action) = stream.next().await {
|
||||
match action {
|
||||
iced_runtime::Action::Widget(mut operation) => {
|
||||
operations += 1;
|
||||
fields.as_widget_mut().operate(
|
||||
&mut tree,
|
||||
Layout::new(&node),
|
||||
&(),
|
||||
operation.as_mut(),
|
||||
);
|
||||
}
|
||||
iced_runtime::Action::Output(message @ Message::Linted(_, _)) => {
|
||||
lint_completions += 1;
|
||||
let _ = app.update(message);
|
||||
}
|
||||
_ => panic!("Unexpected recovery task action"),
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
lint_completions, 1,
|
||||
"Autofocus must not replace request_lint"
|
||||
);
|
||||
assert!(app.lint_running.is_none());
|
||||
let focused = |index: usize| {
|
||||
tree.children[index]
|
||||
.state
|
||||
.downcast_ref::<iced::widget::text_input::State<()>>()
|
||||
.is_focused()
|
||||
};
|
||||
(operations, focused(0), focused(1))
|
||||
}
|
||||
|
||||
fn architecture_recovery_app() -> App {
|
||||
let mut app = App::with_project(Project::empty());
|
||||
let _ = app.update(Message::AddDiagram);
|
||||
let _ = app.update(Message::Architecture(architecture_authoring::Event::Start(
|
||||
Kind::Entity,
|
||||
Some(Position { x: 80., y: 90. }),
|
||||
)));
|
||||
let _ = app.update(Message::Architecture(architecture_authoring::Event::Name(
|
||||
"Accepted team".into(),
|
||||
)));
|
||||
let _ = app.update(Message::Architecture(architecture_authoring::Event::Submit));
|
||||
assert!(app.architecture.is_none());
|
||||
app
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn checkpoint_inline_create_and_rename_restore_focus_without_changing_drafts_or_history()
|
||||
{
|
||||
use architecture_authoring::Event;
|
||||
for rename in [false, true] {
|
||||
let mut app = architecture_recovery_app();
|
||||
let point = Position { x: 439., y: 328.2 };
|
||||
let event = if rename {
|
||||
Event::Rename(app.route_context().unwrap(), app.selected, point)
|
||||
} else {
|
||||
Event::Start(Kind::Actor, Some(point))
|
||||
};
|
||||
let _ = app.update(Message::Architecture(event));
|
||||
let _ = app.update(Message::Architecture(Event::Name(
|
||||
" Recovered engineering name 雨 ".into(),
|
||||
)));
|
||||
let saved = serde_json::to_value(&app.architecture.as_ref().unwrap().saved).unwrap();
|
||||
let snapshot = retained_snapshot(&app).with_architecture(app.architecture.as_ref());
|
||||
let expected = snapshot.clone().restore().unwrap();
|
||||
let checkpoint = snapshot.id;
|
||||
let mut recovered = App::with_project(Project::empty());
|
||||
let task = recovered.checkpoint_loaded(Ok(Arc::new(snapshot)));
|
||||
assert_eq!(
|
||||
run_recovery_tasks(&mut recovered, task).await,
|
||||
(1, true, false)
|
||||
);
|
||||
assert_eq!(recovered.page, Page::Diagram);
|
||||
assert!(recovered.architecture.as_ref().unwrap().inline());
|
||||
assert_eq!(
|
||||
serde_json::to_value(&recovered.architecture.as_ref().unwrap().saved).unwrap(),
|
||||
saved
|
||||
);
|
||||
assert_eq!(recovered.project(), expected.document.project());
|
||||
assert_eq!(recovered.document.bundle(), expected.document.bundle());
|
||||
assert_eq!(recovered.document.revision(), expected.document.revision());
|
||||
assert_eq!(recovered.document.can_undo(), expected.document.can_undo());
|
||||
assert_eq!(recovered.document.can_redo(), expected.document.can_redo());
|
||||
assert_eq!(
|
||||
recovered.source_workspace.bundle(),
|
||||
expected.workspace.bundle()
|
||||
);
|
||||
assert_eq!(recovered.recovery.restored, Some(checkpoint));
|
||||
assert!(recovered.path.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn checkpoint_noninline_contexts_keep_other_focus_and_still_lint() {
|
||||
use architecture_authoring::Event;
|
||||
for context in ["accepted", "armed", "picker", "property", "source"] {
|
||||
let mut app = architecture_recovery_app();
|
||||
match context {
|
||||
"armed" => {
|
||||
let _ = app.update(Message::Architecture(Event::Start(Kind::Actor, None)));
|
||||
}
|
||||
"picker" => {
|
||||
let _ = app.update(Message::Architecture(Event::Existing(None)));
|
||||
}
|
||||
"property" => {
|
||||
app.draft
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.input("/name", "Unapplied property".into());
|
||||
assert!(app.draft_dirty());
|
||||
}
|
||||
"source" => {
|
||||
app.source_workspace
|
||||
.edit("incomplete source 雨".into(), app.source_editor.cursor());
|
||||
assert!(app.source_dirty());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let snapshot = retained_snapshot(&app).with_architecture(app.architecture.as_ref());
|
||||
let expected = snapshot.clone().restore().unwrap();
|
||||
let mut recovered = App::with_project(Project::empty());
|
||||
let task = recovered.checkpoint_loaded(Ok(Arc::new(snapshot)));
|
||||
assert_eq!(
|
||||
run_recovery_tasks(&mut recovered, task).await,
|
||||
(0, false, true),
|
||||
"{context}"
|
||||
);
|
||||
assert!(
|
||||
!recovered
|
||||
.architecture
|
||||
.as_ref()
|
||||
.is_some_and(architecture_authoring::State::inline)
|
||||
);
|
||||
assert_eq!(recovered.project(), expected.document.project());
|
||||
assert_eq!(recovered.document.bundle(), expected.document.bundle());
|
||||
assert_eq!(recovered.document.revision(), expected.document.revision());
|
||||
assert_eq!(
|
||||
recovered.source_workspace.bundle(),
|
||||
expected.workspace.bundle()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saving_an_older_snapshot_keeps_recovery_for_newer_unsaved_work() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
|
||||
@@ -14,7 +14,12 @@ use iced::{
|
||||
};
|
||||
use oa_core::*;
|
||||
use std::collections::BTreeMap;
|
||||
#[cfg(test)]
|
||||
mod contextual_source_tests;
|
||||
pub(crate) mod creation;
|
||||
pub(crate) mod scenario;
|
||||
mod scenario_lifecycle;
|
||||
pub(crate) mod source_draft;
|
||||
pub(crate) mod state_canvas;
|
||||
pub(crate) mod state_edit;
|
||||
pub(crate) mod state_menu;
|
||||
@@ -29,12 +34,12 @@ mod scenario_scroll {
|
||||
use iced::advanced::{Clipboard, Layout, Shell, Widget, layout, renderer, widget};
|
||||
use iced::{Length, Vector, overlay};
|
||||
|
||||
pub(super) fn observe<'a>(
|
||||
pub(super) fn observe<'a, R: renderer::Renderer + 'a>(
|
||||
context: scenario::Context,
|
||||
area: scenario::ScrollArea,
|
||||
restore: Option<scenario::ScrollRestore>,
|
||||
content: impl Into<Element<'a, Message>>,
|
||||
) -> Element<'a, Message> {
|
||||
content: impl Into<Element<'a, Message, Theme, R>>,
|
||||
) -> Element<'a, Message, Theme, R> {
|
||||
Element::new(Observer {
|
||||
content: content.into(),
|
||||
context,
|
||||
@@ -731,16 +736,41 @@ mod scenario_scroll {
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Event {
|
||||
Creation(creation::Event),
|
||||
Topology(topology::Event),
|
||||
Scenario(scenario::Event),
|
||||
States(states::Event),
|
||||
Submit,
|
||||
}
|
||||
impl Event {
|
||||
pub fn is_source_back(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Creation(creation::Event::Source(source_draft::Event::Edit(
|
||||
_,
|
||||
source_draft::Edit::Back
|
||||
))) | Self::Scenario(scenario::Event::Source(source_draft::Event::Edit(
|
||||
_,
|
||||
source_draft::Edit::Back
|
||||
))) | Self::Topology(topology::Event::Source(source_draft::Event::Edit(
|
||||
_,
|
||||
source_draft::Edit::Back
|
||||
)))
|
||||
)
|
||||
}
|
||||
pub fn is_source(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Creation(creation::Event::Source(_))
|
||||
| Self::Scenario(scenario::Event::Source(_))
|
||||
| Self::Topology(topology::Event::Source(_))
|
||||
)
|
||||
}
|
||||
pub fn starts_form(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Topology(topology::Event::Start(..))
|
||||
Self::Creation(creation::Event::Open(..))
|
||||
| Self::Topology(topology::Event::Start(..))
|
||||
| Self::Scenario(scenario::Event::Start(..))
|
||||
| Self::States(states::Event::Start(..) | states::Event::Edit(..))
|
||||
)
|
||||
@@ -749,6 +779,7 @@ impl Event {
|
||||
matches!(
|
||||
self,
|
||||
Self::Submit
|
||||
| Self::Creation(creation::Event::Submit)
|
||||
| Self::Topology(topology::Event::Submit)
|
||||
| Self::Scenario(scenario::Event::Submit)
|
||||
| Self::States(states::Event::Submit)
|
||||
@@ -757,6 +788,7 @@ impl Event {
|
||||
}
|
||||
#[derive(Default)]
|
||||
pub struct State {
|
||||
pub(crate) creation: creation::State,
|
||||
pub active_owner: Option<Id>,
|
||||
pub diagram: state_view::State,
|
||||
pub(crate) topology: topology::State,
|
||||
@@ -773,6 +805,7 @@ struct RecoveryDraft {
|
||||
}
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
enum RecoveryKind {
|
||||
Creation,
|
||||
State,
|
||||
Scenario,
|
||||
Process,
|
||||
@@ -782,6 +815,7 @@ impl State {
|
||||
/// viewport geometry and document/revision tokens never cross a restart.
|
||||
pub fn recovery_snapshot(&self) -> Result<Option<serde_json::Value>, String> {
|
||||
let drafts = [
|
||||
(RecoveryKind::Creation, self.creation.recovery_snapshot()?),
|
||||
(RecoveryKind::State, self.states.recovery_snapshot()?),
|
||||
(RecoveryKind::Scenario, self.scenario.recovery_snapshot()?),
|
||||
(RecoveryKind::Process, self.topology.recovery_snapshot()?),
|
||||
@@ -817,6 +851,9 @@ impl State {
|
||||
}
|
||||
let mut restored = Self::default();
|
||||
match saved.kind {
|
||||
RecoveryKind::Creation => {
|
||||
restored.creation = creation::State::from_recovery(saved.draft)?
|
||||
}
|
||||
RecoveryKind::State => restored.states = states::State::from_recovery(saved.draft)?,
|
||||
RecoveryKind::Scenario => {
|
||||
restored.scenario = scenario::State::from_recovery(saved.draft)?
|
||||
@@ -831,7 +868,7 @@ impl State {
|
||||
}
|
||||
|
||||
pub fn has_pending(&self) -> bool {
|
||||
self.pending_owner().is_some()
|
||||
self.creation.has_pending() || self.pending_owner().is_some()
|
||||
}
|
||||
|
||||
pub fn pending_owner(&self) -> Option<Id> {
|
||||
@@ -844,7 +881,9 @@ impl State {
|
||||
/// Prepare the active form without discarding inputs. The caller must execute
|
||||
/// this exact command and acknowledge only a successful document transaction.
|
||||
pub fn prepare_pending(&self, p: &Project) -> Result<Option<Command>, String> {
|
||||
if self.topology.pending_owner().is_some() {
|
||||
if self.creation.has_pending() {
|
||||
self.creation.prepare_pending(p).map(Some)
|
||||
} else if self.topology.pending_owner().is_some() {
|
||||
self.topology.prepare_pending(p).map(Some)
|
||||
} else if self.scenario.pending_owner().is_some() {
|
||||
self.scenario.prepare_pending(p).map(Some)
|
||||
@@ -857,7 +896,20 @@ impl State {
|
||||
|
||||
/// Clear the active form after its prepared command commits successfully.
|
||||
/// Rejected commands must leave the form available for repair and retry.
|
||||
pub fn source_active(&self) -> bool {
|
||||
self.creation.source_active()
|
||||
|| self.scenario.source_active()
|
||||
|| self.topology.source_active()
|
||||
}
|
||||
pub fn source_open(&self) -> bool {
|
||||
self.creation.source_open() || self.scenario.source_open() || self.topology.source_open()
|
||||
}
|
||||
pub fn source_back(&mut self) -> bool {
|
||||
self.creation.source_back() || self.scenario.source_back() || self.topology.source_back()
|
||||
}
|
||||
|
||||
pub fn acknowledge_commit(&mut self) {
|
||||
self.creation.acknowledge_commit();
|
||||
self.topology.acknowledge_commit();
|
||||
self.scenario.acknowledge_commit();
|
||||
self.states.acknowledge_commit();
|
||||
@@ -867,7 +919,8 @@ impl State {
|
||||
if self.has_pending()
|
||||
&& matches!(
|
||||
&event,
|
||||
Event::Topology(topology::Event::Start(..))
|
||||
Event::Creation(creation::Event::Open(..))
|
||||
| Event::Topology(topology::Event::Start(..))
|
||||
| Event::Scenario(scenario::Event::Start(..))
|
||||
| Event::States(states::Event::Start(..) | states::Event::Edit(..))
|
||||
)
|
||||
@@ -885,6 +938,7 @@ impl State {
|
||||
return Ok(None);
|
||||
}
|
||||
match event {
|
||||
Event::Creation(event) => self.creation.update(p, event),
|
||||
Event::Topology(event) => self.topology.update(p, event),
|
||||
Event::Scenario(event) => self.scenario.update(p, event),
|
||||
Event::States(event) => self.states.update(p, event),
|
||||
@@ -898,6 +952,43 @@ impl State {
|
||||
|
||||
pub fn view(app: &App) -> Element<'_, Message> {
|
||||
let p = app.project();
|
||||
if let Some(card) = app.behavior.creation.view(p) {
|
||||
return card.map(|event| Message::Behavior(Event::Creation(event)));
|
||||
}
|
||||
if let Some(owner) = app.behavior.pending_owner() {
|
||||
let kind = p.elements.get(&owner).map(oa_core::Element::kind);
|
||||
let unavailable = p.layer_of(owner) != Some(app.layer)
|
||||
|| (app.behavior.scenario.pending_owner().is_some() && kind != Some(Kind::Scenario))
|
||||
|| (app.behavior.topology.pending_owner().is_some()
|
||||
&& !matches!(kind, Some(Kind::Process | Kind::FunctionalChain)));
|
||||
if unavailable {
|
||||
let mut out=column![text("The pending behavior owner is unavailable").size(20),text("Your inputs are retained. Return from the source form or cancel this parent draft before choosing another behavior.").size(13)].spacing(12);
|
||||
if let Some(name) = app
|
||||
.behavior
|
||||
.scenario
|
||||
.pending_name()
|
||||
.or_else(|| app.behavior.topology.pending_name())
|
||||
{
|
||||
out =
|
||||
out.push(text(format!("Pending consumer name: {}", short(name, 80))).size(14));
|
||||
}
|
||||
if let Some(child) = app.behavior.scenario.source_view(p) {
|
||||
out = out.push(child.map(|e| Message::Behavior(Event::Scenario(e))));
|
||||
} else if let Some(child) = app.behavior.topology.source_view(p) {
|
||||
out = out.push(child.map(|e| Message::Behavior(Event::Topology(e))));
|
||||
} else {
|
||||
let cancel = if app.behavior.scenario.pending_owner().is_some() {
|
||||
Event::Scenario(scenario::Event::Cancel)
|
||||
} else if app.behavior.topology.pending_owner().is_some() {
|
||||
Event::Topology(topology::Event::Cancel)
|
||||
} else {
|
||||
Event::States(states::Event::Cancel)
|
||||
};
|
||||
out = out.push(button("Cancel retained draft").on_press(Message::Behavior(cancel)));
|
||||
}
|
||||
return container(out).padding(16).width(Fill).height(Fill).into();
|
||||
}
|
||||
}
|
||||
let choices = p
|
||||
.elements
|
||||
.values()
|
||||
@@ -916,23 +1007,40 @@ pub fn view(app: &App) -> Element<'_, Message> {
|
||||
.cloned();
|
||||
let mut out = column![heading("Behavior & scenarios", "Follow exchanges and operating conditions. Select a message, state or transition to edit it.")].spacing(14);
|
||||
let Some(selected) = selected else {
|
||||
return out.push(label("Start with a Process, Scenario or State machine using Create element. Add occurrences or regions inside it to describe behavior.")).padding(24).into();
|
||||
return out
|
||||
.push(label(
|
||||
"Start a named process, scenario or state machine, then model its behavior here.",
|
||||
))
|
||||
.push(action(
|
||||
"New",
|
||||
Message::Behavior(Event::Creation(creation::Event::Open(app.layer))),
|
||||
))
|
||||
.padding(24)
|
||||
.into();
|
||||
};
|
||||
let e = &p.elements[&selected.id];
|
||||
let compact_process = matches!(e.kind(), Kind::Process | Kind::FunctionalChain);
|
||||
let compact_canvas = matches!(
|
||||
e.kind(),
|
||||
Kind::Process | Kind::FunctionalChain | Kind::Scenario
|
||||
);
|
||||
if e.kind() == Kind::StateMachine {
|
||||
out = column![text("States & modes").size(20)].spacing(8);
|
||||
} else if compact_process {
|
||||
// The process canvas owns its compact header and remaining-height layout.
|
||||
} else if compact_canvas {
|
||||
// The process/scenario canvas owns its compact remaining-height layout.
|
||||
out = column![].spacing(6);
|
||||
} else if matches!(e.kind(), Kind::Process | Kind::FunctionalChain) {
|
||||
out = column![text("Processes & functional chains").size(20)].spacing(8);
|
||||
}
|
||||
out = out.push(
|
||||
pick_list(choices, Some(selected), Message::BehaviorOwner)
|
||||
.width(Fill)
|
||||
.text_size(13)
|
||||
.padding(if compact_process { 6 } else { 10 }),
|
||||
row![
|
||||
pick_list(choices, Some(selected), Message::BehaviorOwner)
|
||||
.width(Fill)
|
||||
.text_size(13)
|
||||
.padding(if compact_canvas { 6 } else { 10 }),
|
||||
action(
|
||||
"New",
|
||||
Message::Behavior(Event::Creation(creation::Event::Open(app.layer)))
|
||||
)
|
||||
]
|
||||
.spacing(8),
|
||||
);
|
||||
if matches!(e.kind(), Kind::Process | Kind::FunctionalChain) {
|
||||
return topology::view(app, e.id, out);
|
||||
@@ -1033,6 +1141,17 @@ struct BoxItem {
|
||||
detail: String,
|
||||
marker: Option<Kind>,
|
||||
}
|
||||
fn box_text_clip(rect: Rectangle, scale: f32, viewport: Size) -> Option<Rectangle> {
|
||||
if !scale.is_finite() || scale <= 0. || rect.width <= 20. || rect.height <= 16. {
|
||||
return None;
|
||||
}
|
||||
Rectangle::new(
|
||||
Point::new((rect.x + 10.) * scale, (rect.y + 8.) * scale),
|
||||
Size::new((rect.width - 20.) * scale, (rect.height - 16.) * scale),
|
||||
)
|
||||
.intersection(&Rectangle::new(Point::ORIGIN, viewport))
|
||||
}
|
||||
|
||||
struct Line {
|
||||
id: Id,
|
||||
points: Vec<Point>,
|
||||
@@ -1054,6 +1173,7 @@ struct Scene {
|
||||
rows: BTreeMap<Id, (Id, f32)>,
|
||||
open_calls: Vec<Point>,
|
||||
sequence_bounds: BTreeMap<Id, (f32, f32)>,
|
||||
lifecycle: scenario_lifecycle::render::Rendering,
|
||||
}
|
||||
struct SequenceSlot {
|
||||
slot: scenario::Slot,
|
||||
@@ -1112,6 +1232,7 @@ impl Scene {
|
||||
rows: BTreeMap::new(),
|
||||
open_calls: vec![],
|
||||
sequence_bounds: BTreeMap::new(),
|
||||
lifecycle: scenario_lifecycle::render::Rendering::default(),
|
||||
};
|
||||
let mut lanes = p
|
||||
.children(owner)
|
||||
@@ -1198,7 +1319,17 @@ impl Scene {
|
||||
}
|
||||
let mut y = if scenario { 148. } else { 120. };
|
||||
scene.occurrences(p, owner, scenario, &lane_x, &mut y, 0);
|
||||
scene.size.height = (y + 45.).max(210.);
|
||||
if scenario {
|
||||
scene.lifecycle = scenario_lifecycle::render::Rendering::new(
|
||||
p,
|
||||
owner,
|
||||
&lane_x,
|
||||
&scene.rows,
|
||||
&scene.sequence_bounds,
|
||||
scene.size.height,
|
||||
);
|
||||
scene.lifecycle.align_messages(&mut scene.lines);
|
||||
let calls: Vec<_> = scene
|
||||
.rows
|
||||
.keys()
|
||||
@@ -1223,23 +1354,28 @@ impl Scene {
|
||||
scene.activation_sequence(p, p.elements[&call].parent.unwrap(), call, x, None);
|
||||
}
|
||||
}
|
||||
scene.size.height = (y + 45.).max(210.);
|
||||
for (id, name, detail) in lanes {
|
||||
let x = lane_x[&id];
|
||||
scene.lines.insert(
|
||||
0,
|
||||
Line {
|
||||
id,
|
||||
points: vec![Point::new(x, 85.), Point::new(x, scene.size.height - 20.)],
|
||||
label: String::new(),
|
||||
label_at: Point::ORIGIN,
|
||||
dashed: false,
|
||||
},
|
||||
);
|
||||
if !scenario {
|
||||
scene.lines.insert(
|
||||
0,
|
||||
Line {
|
||||
id,
|
||||
points: vec![Point::new(x, 85.), Point::new(x, scene.size.height - 20.)],
|
||||
label: String::new(),
|
||||
label_at: Point::ORIGIN,
|
||||
dashed: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
scene.boxes.push(BoxItem {
|
||||
id,
|
||||
rect: Rectangle::new(Point::new(x - 102., 15.), Size::new(204., 70.)),
|
||||
title: name,
|
||||
title: if scenario {
|
||||
format!("Lane · {name}")
|
||||
} else {
|
||||
name
|
||||
},
|
||||
detail,
|
||||
marker: None,
|
||||
});
|
||||
@@ -1281,6 +1417,16 @@ impl Scene {
|
||||
let Some(SharedBody::Occurrence { body, .. }) = e.shared() else {
|
||||
continue;
|
||||
};
|
||||
if scenario
|
||||
&& matches!(body, OccurrenceBody::Exchange {
|
||||
sender: Some(sender), receiver: Some(receiver),
|
||||
message_kind: MessageKind::Create | MessageKind::Delete, ..
|
||||
} if sender == receiver)
|
||||
{
|
||||
// Self lifecycle elbows and their caption need clearance from
|
||||
// the preceding action/state/reference box, before recording rows.
|
||||
*y += 16.;
|
||||
}
|
||||
if ordered {
|
||||
let slot = scenario::Slot {
|
||||
parent: owner,
|
||||
@@ -1500,17 +1646,27 @@ impl Scene {
|
||||
});
|
||||
}
|
||||
}
|
||||
fn activation_segment(&mut self, call: Id, x: f32, top: f32, bottom: f32) {
|
||||
if bottom <= top {
|
||||
return;
|
||||
fn activation_segment(
|
||||
&mut self,
|
||||
call: Id,
|
||||
scope: Id,
|
||||
receiver: Id,
|
||||
x: f32,
|
||||
top: f32,
|
||||
bottom: f32,
|
||||
) {
|
||||
for span in self.lifecycle.alive_spans(receiver, scope, top, bottom) {
|
||||
self.boxes.push(BoxItem {
|
||||
id: call,
|
||||
rect: Rectangle::new(
|
||||
Point::new(x - 4., span.start),
|
||||
Size::new(8., span.end - span.start),
|
||||
),
|
||||
title: String::new(),
|
||||
detail: String::new(),
|
||||
marker: None,
|
||||
});
|
||||
}
|
||||
self.boxes.push(BoxItem {
|
||||
id: call,
|
||||
rect: Rectangle::new(Point::new(x - 4., top), Size::new(8., bottom - top)),
|
||||
title: String::new(),
|
||||
detail: String::new(),
|
||||
marker: None,
|
||||
});
|
||||
}
|
||||
/// Follow a single correlation through the ordered tree. Branches are drawn
|
||||
/// as independent segments, never as one chronological stretch through ALT
|
||||
@@ -1523,6 +1679,18 @@ impl Scene {
|
||||
x: f32,
|
||||
mut start: Option<f32>,
|
||||
) -> bool {
|
||||
let Some(SharedBody::Occurrence {
|
||||
body:
|
||||
OccurrenceBody::Exchange {
|
||||
receiver: Some(receiver),
|
||||
..
|
||||
},
|
||||
..
|
||||
}) = p.elements[&call].shared()
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
let receiver = *receiver;
|
||||
let entered_open = start.is_some();
|
||||
let mut split = false;
|
||||
for id in p.elements[&owner].steps().into_iter().flatten() {
|
||||
@@ -1536,6 +1704,21 @@ impl Scene {
|
||||
continue;
|
||||
};
|
||||
match p.elements[id].shared() {
|
||||
Some(SharedBody::Occurrence {
|
||||
body:
|
||||
OccurrenceBody::Exchange {
|
||||
message_kind: MessageKind::Delete,
|
||||
receiver: Some(deleted),
|
||||
..
|
||||
},
|
||||
..
|
||||
}) if *deleted == receiver => {
|
||||
// This path cannot show activation beyond its local death.
|
||||
// Keep the unresolved-call cue; a Delete is not a Reply.
|
||||
self.activation_segment(call, owner, receiver, x, top, y);
|
||||
self.open_calls.push(Point::new(x + 12., y - 16.));
|
||||
start = None;
|
||||
}
|
||||
Some(SharedBody::Occurrence {
|
||||
body:
|
||||
OccurrenceBody::Exchange {
|
||||
@@ -1545,14 +1728,19 @@ impl Scene {
|
||||
},
|
||||
..
|
||||
}) if *correlation == call => {
|
||||
self.activation_segment(call, x, top, y);
|
||||
self.boxes.push(BoxItem {
|
||||
id: call,
|
||||
rect: Rectangle::new(Point::new(x - 7., y - 1.), Size::new(14., 2.)),
|
||||
title: String::new(),
|
||||
detail: String::new(),
|
||||
marker: None,
|
||||
});
|
||||
self.activation_segment(call, owner, receiver, x, top, y);
|
||||
for span in self.lifecycle.alive_spans(receiver, owner, y - 1., y + 1.) {
|
||||
self.boxes.push(BoxItem {
|
||||
id: call,
|
||||
rect: Rectangle::new(
|
||||
Point::new(x - 7., span.start),
|
||||
Size::new(14., span.end - span.start),
|
||||
),
|
||||
title: String::new(),
|
||||
detail: String::new(),
|
||||
marker: None,
|
||||
});
|
||||
}
|
||||
start = None;
|
||||
}
|
||||
Some(SharedBody::Occurrence {
|
||||
@@ -1560,7 +1748,7 @@ impl Scene {
|
||||
..
|
||||
}) => {
|
||||
split = true;
|
||||
self.activation_segment(call, x, top, y);
|
||||
self.activation_segment(call, owner, receiver, x, top, y);
|
||||
let bottom = self
|
||||
.boxes
|
||||
.iter()
|
||||
@@ -1595,7 +1783,7 @@ impl Scene {
|
||||
} else {
|
||||
top + 26.
|
||||
};
|
||||
self.activation_segment(call, x, top, bottom);
|
||||
self.activation_segment(call, owner, receiver, x, top, bottom);
|
||||
self.open_calls.push(Point::new(x + 8., bottom - 7.));
|
||||
}
|
||||
start.is_some()
|
||||
@@ -1628,6 +1816,12 @@ impl Scene {
|
||||
))
|
||||
}
|
||||
fn hit(&self, point: Point) -> Option<Id> {
|
||||
if !point.x.is_finite() || !point.y.is_finite() {
|
||||
return None;
|
||||
}
|
||||
if let Some(id) = self.lifecycle.marker_at(point) {
|
||||
return Some(id);
|
||||
}
|
||||
self.boxes
|
||||
.iter()
|
||||
.rev()
|
||||
@@ -1642,7 +1836,7 @@ impl Scene {
|
||||
.iter()
|
||||
.find(|line| {
|
||||
!line.label.is_empty()
|
||||
&& (Rectangle::new(
|
||||
&& Rectangle::new(
|
||||
line.label_at,
|
||||
Size::new(
|
||||
(line.label.chars().count() as f32 * 6.8 + 8.).min(225.),
|
||||
@@ -1650,13 +1844,10 @@ impl Scene {
|
||||
),
|
||||
)
|
||||
.contains(point)
|
||||
|| line.points.windows(2).any(|p| {
|
||||
crate::diagram::distance_to_segment(point, p[0], p[1])
|
||||
< 8. / self.scale
|
||||
}))
|
||||
})
|
||||
.map(|line| line.id)
|
||||
})
|
||||
.or_else(|| self.near_sequence_item(point))
|
||||
.or_else(|| {
|
||||
self.boxes
|
||||
.iter()
|
||||
@@ -1664,6 +1855,7 @@ impl Scene {
|
||||
.find(|b| b.rect.contains(point) && b.title.is_empty())
|
||||
.map(|b| b.id)
|
||||
})
|
||||
.or_else(|| self.lifecycle.lane_at(point, self.scale))
|
||||
.or_else(|| self.lane_at(point))
|
||||
.or_else(|| {
|
||||
self.lines
|
||||
@@ -1678,6 +1870,34 @@ impl Scene {
|
||||
.map(|line| line.id)
|
||||
})
|
||||
}
|
||||
|
||||
fn near_sequence_item(&self, point: Point) -> Option<Id> {
|
||||
if !self.scale.is_finite() || self.scale <= 0. {
|
||||
return None;
|
||||
}
|
||||
let messages = self
|
||||
.lines
|
||||
.iter()
|
||||
.filter(|line| !line.label.is_empty())
|
||||
.map(|line| {
|
||||
let distance = line
|
||||
.points
|
||||
.windows(2)
|
||||
.map(|p| crate::diagram::distance_to_segment(point, p[0], p[1]))
|
||||
.fold(f32::INFINITY, f32::min);
|
||||
(line.id, distance)
|
||||
});
|
||||
// One distance comparison for both kinds of screen-sized hit tolerance.
|
||||
// Dense minimum-Fit rows must not give the first message or last marker
|
||||
// a larger hit priority than the visibly nearer item. UUID breaks ties.
|
||||
self.lifecycle
|
||||
.nearest_marker(point)
|
||||
.into_iter()
|
||||
.chain(messages)
|
||||
.filter(|(_, distance)| *distance <= 8. / self.scale)
|
||||
.min_by(|a, b| a.1.total_cmp(&b.1).then_with(|| a.0.cmp(&b.0)))
|
||||
.map(|(id, _)| id)
|
||||
}
|
||||
}
|
||||
impl canvas::Program<Message> for Scene {
|
||||
type State = SequenceInput;
|
||||
@@ -1918,6 +2138,7 @@ impl canvas::Program<Message> for Scene {
|
||||
let mut f = Frame::new(renderer, bounds.size());
|
||||
f.fill_rectangle(Point::ORIGIN, bounds.size(), Color::WHITE);
|
||||
f.scale(self.scale);
|
||||
self.lifecycle.draw_lanes(&mut f, self.selected);
|
||||
for b in &self.boxes {
|
||||
let outline = matches!(
|
||||
b.marker,
|
||||
@@ -1996,24 +2217,31 @@ impl canvas::Program<Message> for Scene {
|
||||
}
|
||||
}
|
||||
for b in &self.boxes {
|
||||
f.fill_text(Text {
|
||||
content: short(&b.title, ((b.rect.width - 25.) / 7.) as usize),
|
||||
position: Point::new(b.rect.x + 12., b.rect.y + 12.),
|
||||
size: 13.into(),
|
||||
color: if b.id == self.selected {
|
||||
style::TEAL
|
||||
} else {
|
||||
style::INK
|
||||
},
|
||||
..Default::default()
|
||||
});
|
||||
if !b.detail.is_empty() {
|
||||
f.fill_text(Text {
|
||||
content: short(&b.detail, ((b.rect.width - 25.) / 6.) as usize),
|
||||
position: Point::new(b.rect.x + 12., b.rect.y + 39.),
|
||||
size: 11.into(),
|
||||
color: style::MUTED,
|
||||
..Default::default()
|
||||
if let Some(clip) = box_text_clip(b.rect, self.scale, f.size()) {
|
||||
// Clipped child frames start with an identity transform. Match
|
||||
// the card's scale explicitly, as for lifecycle Create labels.
|
||||
f.with_clip(clip, |label| {
|
||||
label.scale(self.scale);
|
||||
label.fill_text(Text {
|
||||
content: short(&b.title, ((b.rect.width - 25.) / 7.) as usize),
|
||||
position: Point::new(b.rect.x + 12., b.rect.y + 12.),
|
||||
size: 13.into(),
|
||||
color: if b.id == self.selected {
|
||||
style::TEAL
|
||||
} else {
|
||||
style::INK
|
||||
},
|
||||
..Default::default()
|
||||
});
|
||||
if !b.detail.is_empty() {
|
||||
label.fill_text(Text {
|
||||
content: short(&b.detail, ((b.rect.width - 25.) / 6.) as usize),
|
||||
position: Point::new(b.rect.x + 12., b.rect.y + 39.),
|
||||
size: 11.into(),
|
||||
color: style::MUTED,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2026,6 +2254,8 @@ impl canvas::Program<Message> for Scene {
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
self.lifecycle
|
||||
.draw_markers(&mut f, self.selected, self.scale);
|
||||
if let Some((context, tool, editable)) = self.interaction {
|
||||
let active = editable && state.context == Some(context) && state.tool == tool;
|
||||
let point = cursor
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,766 @@
|
||||
//! Local behavior creation. A draft is not a model object: only its validated
|
||||
//! batch may cross Document::execute, and acknowledgement follows success.
|
||||
use iced::{
|
||||
Element as UiElement, Fill,
|
||||
widget::{button, checkbox, column, container, row, scrollable, text, text_input},
|
||||
};
|
||||
use oa_core::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const PAGE_SIZE: usize = 6;
|
||||
pub const NAME_INPUT: &str = "behavior-creation-name";
|
||||
|
||||
// A focused text input consumes Escape. Preserve the application's modal
|
||||
// priority by emitting its ordinary Escape message from this narrow bridge.
|
||||
pub fn subscription(active: bool) -> iced::Subscription<()> {
|
||||
if active {
|
||||
iced::event::listen_with(captured_escape)
|
||||
} else {
|
||||
iced::Subscription::none()
|
||||
}
|
||||
}
|
||||
|
||||
fn captured_escape(
|
||||
event: iced::Event,
|
||||
status: iced::event::Status,
|
||||
_: iced::window::Id,
|
||||
) -> Option<()> {
|
||||
(status == iced::event::Status::Captured
|
||||
&& matches!(
|
||||
event,
|
||||
iced::Event::Keyboard(iced::keyboard::Event::KeyPressed {
|
||||
key: iced::keyboard::Key::Named(iced::keyboard::key::Named::Escape),
|
||||
..
|
||||
})
|
||||
))
|
||||
.then_some(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Family {
|
||||
Sequence,
|
||||
Scenario,
|
||||
Machine,
|
||||
}
|
||||
impl Family {
|
||||
fn kind(self, layer: Layer) -> Kind {
|
||||
match (self, layer) {
|
||||
(Self::Sequence, Layer::Oa) => Kind::Process,
|
||||
(Self::Sequence, Layer::Sa) => Kind::FunctionalChain,
|
||||
(Self::Scenario, _) => Kind::Scenario,
|
||||
(Self::Machine, _) => Kind::StateMachine,
|
||||
}
|
||||
}
|
||||
fn title(self, layer: Layer) -> &'static str {
|
||||
match (self, layer) {
|
||||
(Self::Sequence, Layer::Oa) => "Operational process",
|
||||
(Self::Sequence, Layer::Sa) => "Functional chain",
|
||||
(Self::Scenario, _) => "Scenario",
|
||||
(Self::Machine, _) => "State machine",
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Picker {
|
||||
Owner,
|
||||
Context,
|
||||
}
|
||||
impl Picker {
|
||||
fn index(self) -> usize {
|
||||
match self {
|
||||
Self::Owner => 0,
|
||||
Self::Context => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Event {
|
||||
Source(super::source_draft::Event),
|
||||
Open(Layer),
|
||||
Choose(Family),
|
||||
Name(String),
|
||||
Owner(Id),
|
||||
Viewpoint(ScenarioViewpoint),
|
||||
Context(Option<Id>),
|
||||
ContextOptions(bool),
|
||||
FirstView(bool),
|
||||
Search(Picker, String),
|
||||
Page(Picker, usize),
|
||||
Cancel,
|
||||
Submit,
|
||||
}
|
||||
#[derive(Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct Browser {
|
||||
query: String,
|
||||
page: usize,
|
||||
}
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct Draft {
|
||||
#[serde(default)]
|
||||
source: super::source_draft::State,
|
||||
#[serde(default = "Id::new_v4")]
|
||||
invocation: Id,
|
||||
|
||||
version: u32,
|
||||
document: Id,
|
||||
layer: Layer,
|
||||
family: Option<Family>,
|
||||
name: String,
|
||||
owner: Option<Id>,
|
||||
viewpoint: Option<ScenarioViewpoint>,
|
||||
context: Option<Id>,
|
||||
#[serde(default)]
|
||||
context_open: bool,
|
||||
first_view: bool,
|
||||
element: Id,
|
||||
diagram: Id,
|
||||
placement: Id,
|
||||
browsers: [Browser; 2],
|
||||
}
|
||||
#[derive(Default)]
|
||||
pub struct State {
|
||||
draft: Option<Draft>,
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Created {
|
||||
pub behavior: Id,
|
||||
pub layer: Layer,
|
||||
pub diagram: Option<Id>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn source_active(&self) -> bool {
|
||||
self.draft.as_ref().is_some_and(|d| d.source.engaged())
|
||||
}
|
||||
|
||||
pub fn source_open(&self) -> bool {
|
||||
self.draft.as_ref().is_some_and(|d| d.source.is_open())
|
||||
}
|
||||
pub fn source_back(&mut self) -> bool {
|
||||
self.draft.as_mut().is_some_and(|d| d.source.back())
|
||||
}
|
||||
|
||||
pub fn has_pending(&self) -> bool {
|
||||
self.draft.is_some()
|
||||
}
|
||||
pub fn pending_layer(&self) -> Option<Layer> {
|
||||
self.draft.as_ref().map(|d| d.layer)
|
||||
}
|
||||
/// Capture this before successful acknowledgement; never select these IDs
|
||||
/// on mere planning or a rejected source/model transaction.
|
||||
pub fn created(&self) -> Option<Created> {
|
||||
let d = self.draft.as_ref()?;
|
||||
d.family.map(|family| Created {
|
||||
behavior: d.element,
|
||||
layer: d.layer,
|
||||
diagram: (family == Family::Machine && d.first_view).then_some(d.diagram),
|
||||
})
|
||||
}
|
||||
/// Match before execution, then apply this target only after success.
|
||||
/// Identity alone is insufficient: changed bodies or trailing operations
|
||||
/// must not inherit the prepared creation's selection/opening contract.
|
||||
pub fn created_by(&self, p: &Project, command: &Command) -> Option<Created> {
|
||||
let target = self.created()?;
|
||||
(self.prepare_pending(p).ok().as_ref() == Some(command)).then_some(target)
|
||||
}
|
||||
pub fn recovery_snapshot(&self) -> Result<Option<serde_json::Value>, String> {
|
||||
self.draft
|
||||
.as_ref()
|
||||
.map(serde_json::to_value)
|
||||
.transpose()
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
pub fn from_recovery(value: serde_json::Value) -> Result<Self, String> {
|
||||
let draft: Draft = serde_json::from_value(value).map_err(|e| e.to_string())?;
|
||||
if draft.version != 1 {
|
||||
return Err("Unsupported behavior creation draft version.".into());
|
||||
}
|
||||
// Raw values and stale references remain editable; planning validates
|
||||
// against the current document rather than silently fixing the draft.
|
||||
Ok(Self { draft: Some(draft) })
|
||||
}
|
||||
pub fn acknowledge_commit(&mut self) {
|
||||
self.draft = None;
|
||||
}
|
||||
pub fn prepare_pending(&self, p: &Project) -> Result<Command, String> {
|
||||
self.draft
|
||||
.as_ref()
|
||||
.ok_or("Choose New to start a behavior.")?
|
||||
.command(p)
|
||||
}
|
||||
pub fn update(&mut self, p: &Project, event: Event) -> Result<Option<Command>, String> {
|
||||
if let Event::Source(event) = event {
|
||||
if self
|
||||
.draft
|
||||
.as_mut()
|
||||
.ok_or("Start the consumer form first.")?
|
||||
.source
|
||||
.dismiss(&event)?
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
let d = self
|
||||
.draft
|
||||
.as_ref()
|
||||
.ok_or("Start the consumer form first.")?;
|
||||
if d.family != Some(Family::Machine) {
|
||||
return Err("This reference does not support staged source creation.".into());
|
||||
}
|
||||
if d.document != p.id {
|
||||
return Err("This behavior draft belongs to another document.".into());
|
||||
}
|
||||
let mut next = d.clone();
|
||||
if let Some(id) = next.source.update(
|
||||
p,
|
||||
super::source_draft::Scope::Machine { layer: d.layer },
|
||||
d.invocation,
|
||||
event,
|
||||
)? {
|
||||
next.owner = Some(id);
|
||||
let preview = next
|
||||
.source
|
||||
.preview(p, next.owner)?
|
||||
.ok_or("New owner preview is missing.")?;
|
||||
if !eligible(&preview, next.layer, Picker::Owner, id) {
|
||||
return Err("This participant cannot own the machine.".into());
|
||||
}
|
||||
}
|
||||
self.draft = Some(next);
|
||||
return Ok(None);
|
||||
}
|
||||
if self.source_open() && !matches!(&event, Event::Cancel) {
|
||||
return Err("Use the new source or go Back before editing the parent form.".into());
|
||||
}
|
||||
|
||||
match event {
|
||||
Event::Cancel => {
|
||||
self.draft = None;
|
||||
return Ok(None);
|
||||
}
|
||||
Event::Open(layer) => {
|
||||
if self.has_pending() {
|
||||
return Err("Create or cancel the pending behavior first.".into());
|
||||
}
|
||||
self.draft = Some(Draft {
|
||||
source: Default::default(),
|
||||
invocation: Id::new_v4(),
|
||||
version: 1,
|
||||
document: p.id,
|
||||
layer,
|
||||
family: None,
|
||||
name: String::new(),
|
||||
owner: None,
|
||||
viewpoint: None,
|
||||
context: None,
|
||||
context_open: false,
|
||||
first_view: false,
|
||||
element: Id::new_v4(),
|
||||
diagram: Id::new_v4(),
|
||||
placement: Id::new_v4(),
|
||||
browsers: Default::default(),
|
||||
});
|
||||
return Ok(None);
|
||||
}
|
||||
Event::Submit => return self.prepare_pending(p).map(Some),
|
||||
_ => {}
|
||||
}
|
||||
let d = self
|
||||
.draft
|
||||
.as_mut()
|
||||
.ok_or("Choose New to start a behavior.")?;
|
||||
if d.document != p.id {
|
||||
return Err("This draft belongs to another document. Cancel it to start here.".into());
|
||||
}
|
||||
match event {
|
||||
Event::Choose(family) => {
|
||||
if d.family.is_some_and(|old| old != family) {
|
||||
return Err(
|
||||
"Cancel this draft before choosing a different behavior family.".into(),
|
||||
);
|
||||
}
|
||||
d.family = Some(family);
|
||||
}
|
||||
Event::Name(name) => d.name = name,
|
||||
Event::Owner(id) if d.family == Some(Family::Machine) => {
|
||||
if !eligible(p, d.layer, Picker::Owner, id) {
|
||||
return Err("Choose an existing participant in this layer.".into());
|
||||
}
|
||||
d.owner = Some(id);
|
||||
}
|
||||
Event::Viewpoint(viewpoint) if d.family == Some(Family::Scenario) => {
|
||||
if !viewpoints(d.layer).contains(&viewpoint) {
|
||||
return Err("Choose a scenario viewpoint from this layer.".into());
|
||||
}
|
||||
d.viewpoint = Some(viewpoint);
|
||||
}
|
||||
Event::Context(id) if d.family == Some(Family::Scenario) => {
|
||||
if id.is_some_and(|id| !eligible(p, d.layer, Picker::Context, id)) {
|
||||
return Err("Choose an existing same-layer scenario context.".into());
|
||||
}
|
||||
d.context = id;
|
||||
}
|
||||
Event::ContextOptions(open) if d.family == Some(Family::Scenario) => {
|
||||
d.context_open = open;
|
||||
}
|
||||
Event::FirstView(value) if d.family == Some(Family::Machine) => d.first_view = value,
|
||||
Event::Search(picker, query) => d.browsers[picker.index()] = Browser { query, page: 0 },
|
||||
Event::Page(picker, page) => d.browsers[picker.index()].page = page,
|
||||
Event::Cancel | Event::Open(_) | Event::Submit => unreachable!(),
|
||||
_ => return Err("That field does not apply to this behavior family.".into()),
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn viewpoints(layer: Layer) -> [ScenarioViewpoint; 2] {
|
||||
match layer {
|
||||
Layer::Oa => [
|
||||
ScenarioViewpoint::OperationalActivity,
|
||||
ScenarioViewpoint::ParticipantInteraction,
|
||||
],
|
||||
Layer::Sa => [
|
||||
ScenarioViewpoint::Functional,
|
||||
ScenarioViewpoint::BoundaryInterface,
|
||||
],
|
||||
}
|
||||
}
|
||||
fn viewpoint_title(viewpoint: ScenarioViewpoint) -> &'static str {
|
||||
match viewpoint {
|
||||
ScenarioViewpoint::OperationalActivity => "Activities · operational exchanges",
|
||||
ScenarioViewpoint::ParticipantInteraction => "Participants · interactions",
|
||||
ScenarioViewpoint::Functional => "Functions · functional exchanges",
|
||||
ScenarioViewpoint::BoundaryInterface => "System boundary · interface exchanges",
|
||||
ScenarioViewpoint::MixedLegacy => "Legacy mixed viewpoint",
|
||||
}
|
||||
}
|
||||
fn eligible(p: &Project, layer: Layer, picker: Picker, id: Id) -> bool {
|
||||
p.elements.get(&id).is_some_and(|e| {
|
||||
e.layer() == layer
|
||||
&& match picker {
|
||||
Picker::Owner => validation::parent_permits(e.kind(), Kind::StateMachine),
|
||||
Picker::Context => {
|
||||
e.kind().is_sequence()
|
||||
|| e.kind().is_work()
|
||||
|| e.kind().is_performer()
|
||||
|| matches!(
|
||||
e.kind(),
|
||||
Kind::Capability
|
||||
| Kind::SystemCapability
|
||||
| Kind::OperationalMission
|
||||
| Kind::Mission
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
fn choices<'a>(p: &'a Project, layer: Layer, picker: Picker, query: &str) -> Vec<&'a Element> {
|
||||
let query = query.trim().to_lowercase();
|
||||
let mut matches: Vec<_> = p
|
||||
.elements
|
||||
.values()
|
||||
.filter(|e| eligible(p, layer, picker, e.id))
|
||||
.filter(|e| query.is_empty() || reference_label(p, e.id).to_lowercase().contains(&query))
|
||||
.collect();
|
||||
matches.sort_by(|a, b| {
|
||||
a.name
|
||||
.to_lowercase()
|
||||
.cmp(&b.name.to_lowercase())
|
||||
.then(a.id.cmp(&b.id))
|
||||
});
|
||||
matches
|
||||
}
|
||||
|
||||
fn reference_context(p: &Project, e: &Element) -> String {
|
||||
let mut names = Vec::new();
|
||||
let mut parent = e.parent;
|
||||
let mut seen = std::collections::BTreeSet::new();
|
||||
while let Some(id) = parent {
|
||||
if !seen.insert(id) {
|
||||
break;
|
||||
}
|
||||
let Some(ancestor) = p.elements.get(&id) else {
|
||||
break;
|
||||
};
|
||||
names.push(ancestor.name.as_str());
|
||||
parent = ancestor.parent;
|
||||
}
|
||||
names.reverse();
|
||||
let location = if names.is_empty() {
|
||||
format!("{} root", e.layer())
|
||||
} else {
|
||||
format!("{} / {}", e.layer(), names.join(" / "))
|
||||
};
|
||||
format!("{} · {} · {location}", e.kind(), e.alias)
|
||||
}
|
||||
pub(super) fn reference_label(p: &Project, id: Id) -> String {
|
||||
p.elements
|
||||
.get(&id)
|
||||
.map(|e| format!("{} · {}", e.name, reference_context(p, e)))
|
||||
.unwrap_or_else(|| "Missing element — choose a replacement".into())
|
||||
}
|
||||
impl Draft {
|
||||
fn command(&self, p: &Project) -> Result<Command, String> {
|
||||
self.source.check_scope(
|
||||
super::source_draft::Scope::Machine { layer: self.layer },
|
||||
self.owner,
|
||||
)?;
|
||||
let preview = self.source.preview(p, self.owner)?;
|
||||
let command = self.consumer_command(preview.as_ref().unwrap_or(p))?;
|
||||
self.source.combine(p, self.owner, command)
|
||||
}
|
||||
fn consumer_command(&self, p: &Project) -> Result<Command, String> {
|
||||
if self.document != p.id {
|
||||
return Err("This draft belongs to another document.".into());
|
||||
}
|
||||
let family = self
|
||||
.family
|
||||
.ok_or("Choose the behavior you want to create.")?;
|
||||
if self.name.trim().is_empty() {
|
||||
return Err("Enter the behavior's name before creating it.".into());
|
||||
}
|
||||
if [self.element, self.diagram, self.placement]
|
||||
.into_iter()
|
||||
.any(|id| p.contains(id))
|
||||
|| self.element == self.diagram
|
||||
|| self.element == self.placement
|
||||
|| self.diagram == self.placement
|
||||
{
|
||||
return Err(
|
||||
"A reserved creation identity is already in use. Cancel and start a fresh draft."
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let kind = family.kind(self.layer);
|
||||
let parent = if family == Family::Machine {
|
||||
Some(
|
||||
self.owner
|
||||
.filter(|id| eligible(p, self.layer, Picker::Owner, *id))
|
||||
.ok_or("Choose the participant that owns this state machine.")?,
|
||||
)
|
||||
} else {
|
||||
if self.owner.is_some() {
|
||||
return Err("Processes and scenarios belong at the layer root; context is a separate reference.".into());
|
||||
}
|
||||
None
|
||||
};
|
||||
if family != Family::Machine && self.first_view {
|
||||
return Err("A state view can only accompany a state machine.".into());
|
||||
}
|
||||
let mut body = ElementBody::new(kind, self.layer)
|
||||
.ok_or("This behavior is unavailable in this layer.")?;
|
||||
if family == Family::Scenario {
|
||||
let viewpoint = self
|
||||
.viewpoint
|
||||
.filter(|v| viewpoints(self.layer).contains(v))
|
||||
.ok_or("Choose the scenario's viewpoint explicitly.")?;
|
||||
if self
|
||||
.context
|
||||
.is_some_and(|id| !eligible(p, self.layer, Picker::Context, id))
|
||||
{
|
||||
return Err(
|
||||
"The selected scenario context is missing or no longer compatible.".into(),
|
||||
);
|
||||
}
|
||||
body = ElementBody::shared_in(
|
||||
self.layer,
|
||||
SharedBody::Scenario {
|
||||
viewpoint,
|
||||
context: self.context,
|
||||
precondition: String::new(),
|
||||
postcondition: String::new(),
|
||||
steps: Vec::new(),
|
||||
},
|
||||
);
|
||||
} else if self.viewpoint.is_some() || self.context.is_some() {
|
||||
return Err(
|
||||
"Scenario viewpoint and context cannot be applied to another behavior family."
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let element = Element {
|
||||
id: self.element,
|
||||
alias: p.fresh_alias(&self.name, "behavior", self.element),
|
||||
name: self.name.clone(),
|
||||
description: String::new(),
|
||||
parent,
|
||||
body,
|
||||
};
|
||||
let mut commands = vec![Command::CreateElement(element)];
|
||||
if self.first_view {
|
||||
let working = execute_batch(p, &commands).map_err(|e| e.to_string())?;
|
||||
// Reuse the state editor's geometry contract. A new machine has one
|
||||
// root placement; reserve both presentation IDs across retries.
|
||||
let plan = super::state_edit::create_view(&working, self.element)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let Command::Batch(mut view_commands) = plan.command else {
|
||||
return Err("Unexpected state-view plan.".into());
|
||||
};
|
||||
if view_commands.len() != 2 {
|
||||
return Err("Unexpected additional operation in the first state-view plan.".into());
|
||||
}
|
||||
let Some(Command::Layout(layout)) = view_commands.pop() else {
|
||||
return Err("State-view plan must finish with its layout operation.".into());
|
||||
};
|
||||
let Some(Command::CreateDiagram(mut diagram)) = view_commands.pop() else {
|
||||
return Err("State-view plan is missing its diagram.".into());
|
||||
};
|
||||
if diagram.id != layout || diagram.nodes.len() != 1 {
|
||||
return Err("A first state view must contain only its new machine.".into());
|
||||
}
|
||||
let mut node = diagram
|
||||
.nodes
|
||||
.into_values()
|
||||
.next()
|
||||
.ok_or("State-view root is missing.")?;
|
||||
if node.element != self.element {
|
||||
return Err("State-view root differs from the new machine.".into());
|
||||
}
|
||||
diagram.id = self.diagram;
|
||||
diagram.alias = working.fresh_alias(&diagram.name, "state_view", self.diagram);
|
||||
node.id = self.placement;
|
||||
node.alias = working.fresh_alias(
|
||||
&format!("{} placement", self.name),
|
||||
"placement",
|
||||
self.placement,
|
||||
);
|
||||
diagram.nodes = [(node.id, node)].into_iter().collect();
|
||||
commands.push(Command::CreateDiagram(diagram));
|
||||
commands.push(Command::Layout(self.diagram));
|
||||
}
|
||||
let command = Command::Batch(commands);
|
||||
execute_batch(p, std::slice::from_ref(&command)).map_err(|e| e.to_string())?;
|
||||
Ok(command)
|
||||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
/// Parent maps these local events into the existing guarded Behavior route.
|
||||
/// The card's fields scroll; Create/Cancel stay outside that scroll region.
|
||||
pub fn view<'a>(&'a self, p: &'a Project) -> Option<UiElement<'a, Event>> {
|
||||
self.view_with(p)
|
||||
}
|
||||
fn view_with<'a, R: iced::advanced::text::Renderer + 'a>(
|
||||
&'a self,
|
||||
p: &'a Project,
|
||||
) -> Option<UiElement<'a, Event, iced::Theme, R>> {
|
||||
let d = self.draft.as_ref()?;
|
||||
if let Some(view) = d.source.view(p) {
|
||||
return Some(
|
||||
column![
|
||||
text(format!(
|
||||
"State machine name: {}",
|
||||
crate::views::short(&d.name, 60)
|
||||
))
|
||||
.size(12),
|
||||
view.map(Event::Source)
|
||||
]
|
||||
.spacing(6)
|
||||
.height(Fill)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let mut fields = column![].spacing(10);
|
||||
let title = if let Some(family) = d.family {
|
||||
fields = fields.push(
|
||||
text_input("Exact behavior name", &d.name)
|
||||
.id(NAME_INPUT)
|
||||
.on_input(Event::Name)
|
||||
.on_submit(Event::Submit)
|
||||
.padding(8),
|
||||
);
|
||||
match family {
|
||||
Family::Sequence => {
|
||||
fields = fields.push(
|
||||
text(format!(
|
||||
"Owned by the {} layer root. Add work and exchange contributions after creation.",
|
||||
d.layer
|
||||
))
|
||||
.size(12),
|
||||
);
|
||||
}
|
||||
Family::Scenario => {
|
||||
fields = fields.push(
|
||||
text(format!(
|
||||
"Owned by the {} layer root. Choose what the lifelines represent:",
|
||||
d.layer
|
||||
))
|
||||
.size(12),
|
||||
);
|
||||
for viewpoint in viewpoints(d.layer) {
|
||||
fields = fields.push(
|
||||
button(text(viewpoint_title(viewpoint)).size(13))
|
||||
.style(if d.viewpoint == Some(viewpoint) {
|
||||
button::primary
|
||||
} else {
|
||||
button::secondary
|
||||
})
|
||||
.on_press(Event::Viewpoint(viewpoint))
|
||||
.padding(7),
|
||||
);
|
||||
}
|
||||
let context_label = if d.context_open {
|
||||
"Hide optional context".into()
|
||||
} else if let Some(id) = d.context {
|
||||
format!("Context: {} · Change…", reference_label(p, id))
|
||||
} else {
|
||||
"Add optional context…".into()
|
||||
};
|
||||
fields = fields.push(
|
||||
button(text(context_label).size(12))
|
||||
.on_press(Event::ContextOptions(!d.context_open))
|
||||
.padding(6),
|
||||
);
|
||||
if d.context_open {
|
||||
fields = fields.push(picker_view(p, d, Picker::Context));
|
||||
}
|
||||
}
|
||||
Family::Machine => {
|
||||
if let Some(label) = d.source.label(d.owner) {
|
||||
fields = fields.push(text(label).size(13)).push(text("Create and open commits the new owner and machine together, including the first view only when selected.").size(12));
|
||||
}
|
||||
fields = fields.push(picker_view(p, d, Picker::Owner));
|
||||
fields = fields.push(
|
||||
button("Create new owner…")
|
||||
.on_press(Event::Source(super::source_draft::Event::Open(
|
||||
d.invocation,
|
||||
)))
|
||||
.padding(7),
|
||||
);
|
||||
fields = fields.push(
|
||||
checkbox(d.first_view)
|
||||
.label("Create the first state view")
|
||||
.on_toggle(Event::FirstView),
|
||||
);
|
||||
fields = fields.push(text("The machine starts without regions or states. Add those explicitly in its workspace.").size(12));
|
||||
}
|
||||
}
|
||||
format!("New {}", family.title(d.layer))
|
||||
} else {
|
||||
for family in [Family::Sequence, Family::Scenario, Family::Machine] {
|
||||
let purpose = match family {
|
||||
Family::Sequence => "A path through work and exchanges",
|
||||
Family::Scenario => "Ordered interactions from an explicit viewpoint",
|
||||
Family::Machine => "States and modes of an explicitly chosen participant",
|
||||
};
|
||||
fields = fields.push(
|
||||
button(
|
||||
column![text(family.title(d.layer)).size(15), text(purpose).size(12)]
|
||||
.spacing(3),
|
||||
)
|
||||
.on_press(Event::Choose(family))
|
||||
.padding(10)
|
||||
.width(Fill),
|
||||
);
|
||||
}
|
||||
format!("New behavior · {}", d.layer)
|
||||
};
|
||||
let mut actions =
|
||||
row![button("Cancel · Esc").on_press(Event::Cancel).padding(8)].spacing(8);
|
||||
if d.family.is_some() {
|
||||
actions = actions.push(button("Create and open").on_press(Event::Submit).padding(8));
|
||||
}
|
||||
Some(
|
||||
container(
|
||||
column![
|
||||
text(title).size(20),
|
||||
scrollable(fields)
|
||||
.id("behavior-creation-fields")
|
||||
.height(Fill),
|
||||
container(actions).id("behavior-creation-actions")
|
||||
]
|
||||
.spacing(12),
|
||||
)
|
||||
.padding(18)
|
||||
.max_width(680)
|
||||
.height(Fill)
|
||||
.style(crate::style::card)
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
fn picker_view<'a, R: iced::advanced::text::Renderer + 'a>(
|
||||
p: &'a Project,
|
||||
d: &'a Draft,
|
||||
picker: Picker,
|
||||
) -> UiElement<'a, Event, iced::Theme, R> {
|
||||
let browser = &d.browsers[picker.index()];
|
||||
let options = choices(p, d.layer, picker, &browser.query);
|
||||
let page = browser
|
||||
.page
|
||||
.min(options.len().saturating_sub(1) / PAGE_SIZE);
|
||||
let selected = match picker {
|
||||
Picker::Owner => d.owner,
|
||||
Picker::Context => d.context,
|
||||
};
|
||||
let staged_owner = picker == Picker::Owner && d.source.label(selected).is_some();
|
||||
let title = match picker {
|
||||
Picker::Owner if staged_owner => "Or choose an existing participant",
|
||||
Picker::Owner => "Owned by participant",
|
||||
Picker::Context => "Scenario context · optional",
|
||||
};
|
||||
let mut out = column![
|
||||
text(title).size(13),
|
||||
text_input("Search names, aliases or parent context", &browser.query)
|
||||
.on_input(move |query| Event::Search(picker, query))
|
||||
.padding(6)
|
||||
]
|
||||
.spacing(5);
|
||||
if let Some(id) = selected
|
||||
&& !staged_owner
|
||||
{
|
||||
out = out.push(text(format!("Selected: {}", reference_label(p, id))).size(12));
|
||||
} else if picker == Picker::Context {
|
||||
out = out.push(text("No context selected").size(12));
|
||||
}
|
||||
if options.is_empty() {
|
||||
out = out.push(text(match picker {
|
||||
Picker::Owner if staged_owner => "No existing participants match this search. The new owner above remains selected.",
|
||||
Picker::Owner => "No matching participant. Refine the search or choose Create new owner.",
|
||||
Picker::Context => "No matching context. Refine the search, or leave this optional reference empty.",
|
||||
}).size(12));
|
||||
}
|
||||
for e in options.iter().skip(page * PAGE_SIZE).take(PAGE_SIZE) {
|
||||
let message = match picker {
|
||||
Picker::Owner => Event::Owner(e.id),
|
||||
Picker::Context => Event::Context(Some(e.id)),
|
||||
};
|
||||
out = out.push(
|
||||
container(
|
||||
button(
|
||||
column![
|
||||
text(&e.name).size(13),
|
||||
text(reference_context(p, e)).size(11)
|
||||
]
|
||||
.spacing(2),
|
||||
)
|
||||
.style(if selected == Some(e.id) {
|
||||
button::primary
|
||||
} else {
|
||||
button::secondary
|
||||
})
|
||||
.on_press(message)
|
||||
.padding(6)
|
||||
.width(Fill),
|
||||
)
|
||||
.id(format!("behavior-creation-candidate-{}", e.id)),
|
||||
);
|
||||
}
|
||||
let mut navigation = row![text(format!("{} matches", options.len())).size(11)].spacing(8);
|
||||
if page > 0 {
|
||||
navigation = navigation.push(button("Previous").on_press(Event::Page(picker, page - 1)));
|
||||
}
|
||||
if (page + 1) * PAGE_SIZE < options.len() {
|
||||
navigation = navigation.push(button("Next").on_press(Event::Page(picker, page + 1)));
|
||||
}
|
||||
if picker == Picker::Context && selected.is_some() {
|
||||
navigation = navigation.push(button("Clear context").on_press(Event::Context(None)));
|
||||
}
|
||||
out.push(navigation).into()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod app_tests;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
@@ -0,0 +1,340 @@
|
||||
//! App-boundary regressions for the coordinated parent wiring. No I/O task is
|
||||
//! polled: file dialogs, checkpoint writes and lesson loading remain inactive.
|
||||
use super::*;
|
||||
use crate::app::{App, Message, Page, Pending};
|
||||
use std::sync::Arc;
|
||||
|
||||
fn send(app: &mut App, event: Event) {
|
||||
let _ = app.update(Message::Behavior(crate::behavior::Event::Creation(event)));
|
||||
}
|
||||
fn raw_property(app: &App) -> serde_json::Value {
|
||||
serde_json::to_value(app.draft.as_ref().unwrap().recovery_snapshot()).unwrap()
|
||||
}
|
||||
fn begin(app: &mut App, layer: Layer, family: Family, name: &str) {
|
||||
let _ = app.update(Message::Layer(layer));
|
||||
let _ = app.update(Message::Page(Page::Behavior));
|
||||
send(app, Event::Open(layer));
|
||||
send(app, Event::Choose(family));
|
||||
send(app, Event::Name(name.into()));
|
||||
}
|
||||
|
||||
fn focused_escape() -> Message {
|
||||
use iced::advanced::{Layout, Shell, layout, widget};
|
||||
use iced::keyboard::{self, key::Named};
|
||||
let mut input: iced::Element<'_, (), iced::Theme, ()> = text_input("Name", "Unfinished name")
|
||||
.id(NAME_INPUT)
|
||||
.on_input(|_| ())
|
||||
.into();
|
||||
let mut tree = widget::Tree::new(input.as_widget());
|
||||
let size = iced::Size::new(300., 40.);
|
||||
let node =
|
||||
input
|
||||
.as_widget_mut()
|
||||
.layout(&mut tree, &(), &layout::Limits::new(iced::Size::ZERO, size));
|
||||
tree.state
|
||||
.downcast_mut::<iced::widget::text_input::State<()>>()
|
||||
.focus();
|
||||
let event = iced::Event::Keyboard(keyboard::Event::KeyPressed {
|
||||
key: keyboard::Key::Named(Named::Escape),
|
||||
modified_key: keyboard::Key::Named(Named::Escape),
|
||||
physical_key: keyboard::key::Physical::Code(keyboard::key::Code::Escape),
|
||||
location: keyboard::Location::Standard,
|
||||
modifiers: keyboard::Modifiers::empty(),
|
||||
text: None,
|
||||
repeat: false,
|
||||
});
|
||||
let mut messages = Vec::new();
|
||||
let mut shell = Shell::new(&mut messages);
|
||||
input.as_widget_mut().update(
|
||||
&mut tree,
|
||||
&event,
|
||||
Layout::new(&node),
|
||||
iced::mouse::Cursor::Unavailable,
|
||||
&(),
|
||||
&mut iced::advanced::clipboard::Null,
|
||||
&mut shell,
|
||||
&iced::Rectangle::new(iced::Point::ORIGIN, size),
|
||||
);
|
||||
assert!(shell.is_event_captured());
|
||||
assert_eq!(
|
||||
captured_escape(
|
||||
event.clone(),
|
||||
iced::event::Status::Ignored,
|
||||
iced::window::Id::unique()
|
||||
),
|
||||
None
|
||||
);
|
||||
captured_escape(
|
||||
event,
|
||||
iced::event::Status::Captured,
|
||||
iced::window::Id::unique(),
|
||||
)
|
||||
.unwrap();
|
||||
Message::Escape
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn app_first_captured_escape_dismisses_file_or_pending_overlay_before_creation() {
|
||||
for file in [false, true] {
|
||||
let mut app = App::with_project(Project::empty());
|
||||
begin(
|
||||
&mut app,
|
||||
Layer::Oa,
|
||||
Family::Sequence,
|
||||
"Keep while overlay closes",
|
||||
);
|
||||
let raw = app.behavior.recovery_snapshot().unwrap();
|
||||
let accepted = app.document.snapshot();
|
||||
if file {
|
||||
app.file_prompt = Some(crate::app::FilePrompt {
|
||||
operation: crate::app::FileOperation::Save,
|
||||
path: "unsubmitted-name.arcadia".into(),
|
||||
overwrite: false,
|
||||
});
|
||||
} else {
|
||||
app.pending = Some(Pending::Walkthrough);
|
||||
}
|
||||
let _ = app.update(focused_escape());
|
||||
assert!(app.file_prompt.is_none());
|
||||
assert!(app.pending.is_none());
|
||||
assert_eq!(app.behavior.recovery_snapshot().unwrap(), raw);
|
||||
assert!(Arc::ptr_eq(&accepted, &app.document.snapshot()));
|
||||
let _ = app.update(focused_escape());
|
||||
assert!(!app.behavior.has_pending());
|
||||
assert!(Arc::ptr_eq(&accepted, &app.document.snapshot()));
|
||||
assert!(!app.document.can_undo());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn app_creation_opens_the_actual_new_owner_instead_of_first_or_previously_active_behavior() {
|
||||
for layer in [Layer::Oa, Layer::Sa] {
|
||||
let mut p = Project::empty();
|
||||
let old = p
|
||||
.add_element(Family::Sequence.kind(layer), layer, "Old behavior", None)
|
||||
.unwrap();
|
||||
let mut app = App::with_project(p.clone());
|
||||
let _ = app.update(Message::Select(old));
|
||||
begin(&mut app, layer, Family::Sequence, " New exact path 雨 ");
|
||||
let target = app.behavior.creation.created().unwrap();
|
||||
assert_ne!(target.behavior, old);
|
||||
let before = app.document.bundle().clone();
|
||||
send(&mut app, Event::Submit);
|
||||
assert_eq!(app.page, Page::Behavior);
|
||||
assert_eq!(app.layer, layer);
|
||||
assert_eq!(app.selected, target.behavior);
|
||||
assert_eq!(app.behavior.active_owner, Some(target.behavior));
|
||||
assert_eq!(crate::behavior::selected_owner(&app), Some(target.behavior));
|
||||
assert_eq!(
|
||||
app.project().elements[&target.behavior].name,
|
||||
" New exact path 雨 "
|
||||
);
|
||||
assert!(!app.behavior.has_pending());
|
||||
let after = app.document.bundle().clone();
|
||||
let _ = app.update(Message::Undo);
|
||||
assert_eq!(app.document.bundle(), &before);
|
||||
assert!(!app.document.can_undo());
|
||||
let _ = app.update(Message::Redo);
|
||||
assert_eq!(app.document.bundle(), &after);
|
||||
assert_eq!(app.project().elements[&old], p.elements[&old]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn app_machine_behavior_submit_opens_its_opted_in_view_as_one_undoable_transaction() {
|
||||
for first_view in [false, true] {
|
||||
let mut p = Project::empty();
|
||||
let owner = p
|
||||
.add_element(Kind::System, Layer::Sa, "Flight system", None)
|
||||
.unwrap();
|
||||
let mut app = App::with_project(p.clone());
|
||||
begin(&mut app, Layer::Sa, Family::Machine, " Flight modes 雨 ");
|
||||
send(&mut app, Event::Owner(owner));
|
||||
send(&mut app, Event::FirstView(first_view));
|
||||
let target = app.behavior.creation.created().unwrap();
|
||||
let before = app.document.bundle().clone();
|
||||
let _ = app.update(Message::Behavior(crate::behavior::Event::Submit));
|
||||
assert!(!app.behavior.has_pending(), "{}", app.status);
|
||||
assert_eq!(app.selected, target.behavior);
|
||||
assert_eq!(app.behavior.active_owner, Some(target.behavior));
|
||||
assert_eq!(app.behavior.diagram.diagram, target.diagram);
|
||||
assert_eq!(
|
||||
crate::behavior::state_view::diagram(&app, target.behavior),
|
||||
target.diagram
|
||||
);
|
||||
assert_eq!(app.project().elements[&target.behavior].parent, Some(owner));
|
||||
assert_eq!(app.page, Page::Behavior);
|
||||
let after = app.document.bundle().clone();
|
||||
let _ = app.update(Message::Undo);
|
||||
assert_eq!(app.document.bundle(), &before);
|
||||
assert_eq!(app.project(), &p);
|
||||
assert!(!app.document.can_undo());
|
||||
let _ = app.update(Message::Redo);
|
||||
assert_eq!(app.document.bundle(), &after);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn app_stale_layer_open_never_starts_or_replaces_a_draft() {
|
||||
let mut app = App::with_project(Project::empty());
|
||||
let _ = app.update(Message::Layer(Layer::Sa));
|
||||
app.page = Page::Behavior;
|
||||
let accepted = app.document.snapshot();
|
||||
send(&mut app, Event::Open(Layer::Oa));
|
||||
assert!(!app.behavior.has_pending());
|
||||
assert_eq!(app.layer, Layer::Sa);
|
||||
send(&mut app, Event::Open(Layer::Sa));
|
||||
send(&mut app, Event::Choose(Family::Sequence));
|
||||
send(&mut app, Event::Name("Retained system chain".into()));
|
||||
let raw = app.behavior.recovery_snapshot().unwrap();
|
||||
send(&mut app, Event::Open(Layer::Oa));
|
||||
assert_eq!(app.behavior.recovery_snapshot().unwrap(), raw);
|
||||
assert!(Arc::ptr_eq(&accepted, &app.document.snapshot()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn app_new_never_auto_applies_valid_or_invalid_property_and_source_drafts() {
|
||||
for dirty in [
|
||||
"property-valid",
|
||||
"property-invalid",
|
||||
"source-valid",
|
||||
"source-invalid",
|
||||
] {
|
||||
let mut app = App::with_project(Project::empty());
|
||||
app.page = Page::Behavior;
|
||||
match dirty {
|
||||
"property-valid" => app
|
||||
.draft
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.input("/name", "Raw property name".into()),
|
||||
"property-invalid" => app.draft.as_mut().unwrap().input("/id", "-".into()),
|
||||
"source-valid" => app.source_workspace.edit(
|
||||
format!("{}\n// Retained comment\n", app.source_workspace.text()),
|
||||
app.source_editor.cursor(),
|
||||
),
|
||||
_ => app
|
||||
.source_workspace
|
||||
.edit("unfinished source 雨".into(), app.source_editor.cursor()),
|
||||
}
|
||||
let accepted = app.document.snapshot();
|
||||
let property = raw_property(&app);
|
||||
let workspace = app.source_workspace.recovery_snapshot();
|
||||
send(&mut app, Event::Open(Layer::Oa));
|
||||
assert!(
|
||||
!app.behavior.creation.has_pending(),
|
||||
"{dirty}: New must refuse without applying another editor"
|
||||
);
|
||||
assert!(Arc::ptr_eq(&accepted, &app.document.snapshot()));
|
||||
assert_eq!(raw_property(&app), property);
|
||||
assert_eq!(
|
||||
serde_json::to_value(app.source_workspace.recovery_snapshot()).unwrap(),
|
||||
serde_json::to_value(workspace).unwrap()
|
||||
);
|
||||
assert!(!app.document.can_undo());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn app_choosing_another_behavior_or_layer_cannot_commit_a_creation_draft() {
|
||||
let mut p = Project::empty();
|
||||
let old = p
|
||||
.add_element(Kind::Process, Layer::Oa, "Old process", None)
|
||||
.unwrap();
|
||||
let mut app = App::with_project(p);
|
||||
begin(&mut app, Layer::Oa, Family::Sequence, "Retain this process");
|
||||
let raw = app.behavior.recovery_snapshot().unwrap();
|
||||
let accepted = app.document.snapshot();
|
||||
let _ = app.update(Message::Layer(Layer::Sa));
|
||||
let _ = app.update(Message::BehaviorOwner(crate::app::Choice {
|
||||
id: old,
|
||||
label: "Old process".into(),
|
||||
}));
|
||||
assert_eq!(app.layer, Layer::Oa);
|
||||
assert_eq!(app.behavior.recovery_snapshot().unwrap(), raw);
|
||||
assert!(Arc::ptr_eq(&accepted, &app.document.snapshot()));
|
||||
let _ = app.update(Message::Escape);
|
||||
assert!(!app.behavior.has_pending());
|
||||
assert!(Arc::ptr_eq(&accepted, &app.document.snapshot()));
|
||||
assert!(!app.document.can_undo());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn app_lesson_request_cancel_retains_creation_then_escape_discards_only_the_draft() {
|
||||
let mut app = App::with_project(Project::empty());
|
||||
begin(
|
||||
&mut app,
|
||||
Layer::Oa,
|
||||
Family::Sequence,
|
||||
"Unfinished engineering path",
|
||||
);
|
||||
let raw = app.behavior.recovery_snapshot().unwrap();
|
||||
let accepted = app.document.snapshot();
|
||||
let task = app.update(Message::Home(crate::home::Event::Walkthrough));
|
||||
assert!(matches!(app.pending, Some(Pending::Walkthrough)));
|
||||
assert!(iced_runtime::task::into_stream(task).is_none());
|
||||
let _ = app.update(Message::Escape);
|
||||
assert!(app.pending.is_none());
|
||||
assert_eq!(app.behavior.recovery_snapshot().unwrap(), raw);
|
||||
assert!(Arc::ptr_eq(&accepted, &app.document.snapshot()));
|
||||
let _ = app.update(Message::Escape);
|
||||
assert!(!app.behavior.has_pending());
|
||||
assert!(Arc::ptr_eq(&accepted, &app.document.snapshot()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn app_recovered_rootless_sa_creation_retains_layer_ids_and_other_raw_drafts() {
|
||||
let mut app = App::with_project(Project::empty());
|
||||
begin(
|
||||
&mut app,
|
||||
Layer::Sa,
|
||||
Family::Scenario,
|
||||
" Recovered system scenario 雨 ",
|
||||
);
|
||||
send(
|
||||
&mut app,
|
||||
Event::Viewpoint(ScenarioViewpoint::BoundaryInterface),
|
||||
);
|
||||
// Recovery can contain simultaneous drafts even though new input is guarded.
|
||||
app.draft
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.input("/name", "Retained property".into());
|
||||
app.source_workspace.edit(
|
||||
"unfinished source preserved".into(),
|
||||
app.source_editor.cursor(),
|
||||
);
|
||||
let snapshot = crate::recovery::Snapshot::capture(
|
||||
app.canvas_document,
|
||||
&app.document,
|
||||
&app.source_workspace,
|
||||
app.draft.as_ref(),
|
||||
&app.behavior,
|
||||
None,
|
||||
app.selected,
|
||||
)
|
||||
.unwrap();
|
||||
let expected = snapshot.clone().restore().unwrap();
|
||||
let raw = app.behavior.recovery_snapshot().unwrap();
|
||||
let mut restored = App::with_project(Project::empty());
|
||||
let _ = restored.update(Message::CheckpointLoaded(Ok(Arc::new(snapshot))));
|
||||
assert_eq!(restored.page, Page::Behavior);
|
||||
assert_eq!(restored.layer, Layer::Sa);
|
||||
assert_eq!(restored.behavior.recovery_snapshot().unwrap(), raw);
|
||||
assert_eq!(
|
||||
restored.behavior.creation.created(),
|
||||
app.behavior.creation.created()
|
||||
);
|
||||
assert_eq!(restored.project(), expected.document.project());
|
||||
assert_eq!(restored.document.bundle(), expected.document.bundle());
|
||||
assert_eq!(
|
||||
restored.source_workspace.bundle(),
|
||||
expected.workspace.bundle()
|
||||
);
|
||||
assert_eq!(raw_property(&restored), raw_property(&app));
|
||||
let before = restored.document.snapshot();
|
||||
send(&mut restored, Event::Submit);
|
||||
assert!(Arc::ptr_eq(&before, &restored.document.snapshot()));
|
||||
assert_eq!(restored.behavior.recovery_snapshot().unwrap(), raw);
|
||||
assert_eq!(raw_property(&restored), raw_property(&app));
|
||||
}
|
||||
@@ -0,0 +1,599 @@
|
||||
use super::*;
|
||||
use crate::document::Document;
|
||||
use oa_core::source_project::ProjectLimits;
|
||||
|
||||
fn start(p: &Project, layer: Layer, family: Family, name: &str) -> State {
|
||||
let mut state = State::default();
|
||||
assert!(state.update(p, Event::Open(layer)).unwrap().is_none());
|
||||
state.update(p, Event::Choose(family)).unwrap();
|
||||
state.update(p, Event::Name(name.into())).unwrap();
|
||||
state
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mounted_creation_keeps_actions_visible_and_renders_only_six_owner_candidates() {
|
||||
use iced::advanced::{Layout, layout, widget};
|
||||
use iced::{Rectangle, Size, Vector};
|
||||
struct Geometry {
|
||||
candidates: Vec<widget::Id>,
|
||||
rendered: Vec<widget::Id>,
|
||||
actions: Option<Rectangle>,
|
||||
fields: Option<Rectangle>,
|
||||
}
|
||||
impl widget::Operation for Geometry {
|
||||
fn traverse(&mut self, visit: &mut dyn FnMut(&mut dyn widget::Operation)) {
|
||||
visit(self);
|
||||
}
|
||||
fn container(&mut self, id: Option<&widget::Id>, bounds: Rectangle) {
|
||||
if id == Some(&widget::Id::new("behavior-creation-actions")) {
|
||||
self.actions = Some(bounds);
|
||||
}
|
||||
if let Some(id) = id.filter(|id| self.candidates.contains(id)) {
|
||||
self.rendered.push(id.clone());
|
||||
}
|
||||
}
|
||||
fn scrollable(
|
||||
&mut self,
|
||||
id: Option<&widget::Id>,
|
||||
bounds: Rectangle,
|
||||
_: Rectangle,
|
||||
_: Vector,
|
||||
_: &mut dyn widget::operation::Scrollable,
|
||||
) {
|
||||
if id == Some(&widget::Id::new("behavior-creation-fields")) {
|
||||
self.fields = Some(bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut p = Project::empty();
|
||||
for index in 0..105 {
|
||||
p.add_element(
|
||||
Kind::Entity,
|
||||
Layer::Oa,
|
||||
&format!(
|
||||
"{index:03} Long engineering participant name {}",
|
||||
"雨 extended ".repeat(20)
|
||||
),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
let candidates: Vec<_> = p
|
||||
.elements
|
||||
.keys()
|
||||
.map(|id| widget::Id::from(format!("behavior-creation-candidate-{id}")))
|
||||
.collect();
|
||||
for family in [Family::Sequence, Family::Scenario, Family::Machine] {
|
||||
let state = start(&p, Layer::Oa, family, "Very long exact behavior name 雨");
|
||||
for size in [
|
||||
Size::new(500., 570.),
|
||||
Size::new(1050., 570.),
|
||||
Size::new(500., 320.),
|
||||
] {
|
||||
let mut view: UiElement<'_, Event, iced::Theme, ()> = state.view_with(&p).unwrap();
|
||||
let mut tree = widget::Tree::new(view.as_widget());
|
||||
let node =
|
||||
view.as_widget_mut()
|
||||
.layout(&mut tree, &(), &layout::Limits::new(Size::ZERO, size));
|
||||
let mut geometry = Geometry {
|
||||
candidates: candidates.clone(),
|
||||
rendered: Vec::new(),
|
||||
actions: None,
|
||||
fields: None,
|
||||
};
|
||||
view.as_widget_mut()
|
||||
.operate(&mut tree, Layout::new(&node), &(), &mut geometry);
|
||||
let actions = geometry.actions.unwrap();
|
||||
let fields = geometry.fields.unwrap();
|
||||
assert!(actions.x >= 0. && actions.y >= 0.);
|
||||
assert!(
|
||||
actions.x + actions.width <= size.width
|
||||
&& actions.y + actions.height <= size.height
|
||||
);
|
||||
assert!(fields.height >= 100. && fields.y + fields.height <= actions.y);
|
||||
assert_eq!(
|
||||
geometry.rendered.len(),
|
||||
if family == Family::Machine { 6 } else { 0 }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn roundtrip(p: Project, state: &mut State) -> Project {
|
||||
let command = state.update(&p, Event::Submit).unwrap().unwrap();
|
||||
assert_eq!(
|
||||
command,
|
||||
state.prepare_pending(&p).unwrap(),
|
||||
"preparation must retain reserved IDs"
|
||||
);
|
||||
let api: Command = serde_json::from_value(serde_json::to_value(&command).unwrap()).unwrap();
|
||||
assert_eq!(api, command);
|
||||
let expected = execute_batch(&p, &[api]).unwrap();
|
||||
let target = state.created().unwrap();
|
||||
let mut document = Document::from_project(p.clone(), ProjectLimits::default()).unwrap();
|
||||
let before_source = document.bundle().clone();
|
||||
document.execute(command).unwrap();
|
||||
assert_eq!(document.project(), &expected);
|
||||
assert!(
|
||||
state.has_pending(),
|
||||
"only successful acknowledgement clears raw fields"
|
||||
);
|
||||
state.acknowledge_commit();
|
||||
assert!(!state.has_pending());
|
||||
assert!(document.project().elements.contains_key(&target.behavior));
|
||||
let after_source = document.bundle().clone();
|
||||
assert!(document.undo());
|
||||
assert_eq!(document.project(), &p);
|
||||
assert_eq!(document.bundle(), &before_source);
|
||||
assert!(!document.can_undo(), "one creation must be one Undo entry");
|
||||
assert!(document.redo());
|
||||
assert_eq!(document.project(), &expected);
|
||||
assert_eq!(document.bundle(), &after_source);
|
||||
let reopened = Document::from_bundle(after_source, ProjectLimits::default()).unwrap();
|
||||
assert_eq!(reopened.project(), &expected);
|
||||
expected
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn oa_process_and_sa_chain_keep_exact_names_root_ownership_and_one_history_entry() {
|
||||
for layer in [Layer::Oa, Layer::Sa] {
|
||||
let p = Project::empty();
|
||||
let name = " Supply \"water\" — режим ";
|
||||
let mut state = start(&p, layer, Family::Sequence, name);
|
||||
let target = state.created().unwrap();
|
||||
let result = roundtrip(p, &mut state);
|
||||
let element = &result.elements[&target.behavior];
|
||||
assert_eq!(element.name, name);
|
||||
assert_eq!(element.layer(), layer);
|
||||
assert_eq!(
|
||||
element.kind(),
|
||||
if layer == Layer::Oa {
|
||||
Kind::Process
|
||||
} else {
|
||||
Kind::FunctionalChain
|
||||
}
|
||||
);
|
||||
assert_eq!(element.parent, None);
|
||||
assert!(element.steps().unwrap().is_empty());
|
||||
assert!(result.diagrams.is_empty());
|
||||
assert!(result.relations.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_four_viewpoints_are_explicit_and_survive_source_history_reopen() {
|
||||
for layer in [Layer::Oa, Layer::Sa] {
|
||||
for viewpoint in viewpoints(layer) {
|
||||
let mut p = Project::empty();
|
||||
let context = p
|
||||
.add_element(
|
||||
if layer == Layer::Oa {
|
||||
Kind::Capability
|
||||
} else {
|
||||
Kind::SystemCapability
|
||||
},
|
||||
layer,
|
||||
"Water supply",
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
let mut state = start(&p, layer, Family::Scenario, "Nominal supply");
|
||||
assert!(
|
||||
state
|
||||
.prepare_pending(&p)
|
||||
.unwrap_err()
|
||||
.contains("explicitly")
|
||||
);
|
||||
assert!(
|
||||
state
|
||||
.update(&p, Event::Viewpoint(ScenarioViewpoint::MixedLegacy))
|
||||
.is_err()
|
||||
);
|
||||
let wrong = viewpoints(if layer == Layer::Oa {
|
||||
Layer::Sa
|
||||
} else {
|
||||
Layer::Oa
|
||||
})[0];
|
||||
assert!(state.update(&p, Event::Viewpoint(wrong)).is_err());
|
||||
state.update(&p, Event::Viewpoint(viewpoint)).unwrap();
|
||||
state.update(&p, Event::Context(Some(context))).unwrap();
|
||||
let id = state.created().unwrap().behavior;
|
||||
let result = roundtrip(p, &mut state);
|
||||
assert_eq!(
|
||||
result.elements[&id].parent, None,
|
||||
"context is not containment"
|
||||
);
|
||||
assert!(
|
||||
matches!(result.elements[&id].shared(), Some(SharedBody::Scenario { viewpoint: actual, context: Some(actual_context), steps, .. }) if *actual == viewpoint && *actual_context == context && steps.is_empty())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn machine_requires_valid_owner_and_first_view_adds_no_region_or_state() {
|
||||
for layer in [Layer::Oa, Layer::Sa] {
|
||||
for first_view in [false, true] {
|
||||
let mut p = Project::empty();
|
||||
let owner = p
|
||||
.add_element(
|
||||
if layer == Layer::Oa {
|
||||
Kind::Entity
|
||||
} else {
|
||||
Kind::System
|
||||
},
|
||||
layer,
|
||||
"Pump",
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
let wrong_kind = p
|
||||
.add_element(
|
||||
if layer == Layer::Oa {
|
||||
Kind::Activity
|
||||
} else {
|
||||
Kind::Function
|
||||
},
|
||||
layer,
|
||||
"Pump water",
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
let opposite = if layer == Layer::Oa {
|
||||
Layer::Sa
|
||||
} else {
|
||||
Layer::Oa
|
||||
};
|
||||
let wrong_layer = p
|
||||
.add_element(
|
||||
if opposite == Layer::Oa {
|
||||
Kind::Entity
|
||||
} else {
|
||||
Kind::System
|
||||
},
|
||||
opposite,
|
||||
"Other layer pump",
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
let mut state = start(&p, layer, Family::Machine, "Pump modes");
|
||||
assert!(
|
||||
state
|
||||
.prepare_pending(&p)
|
||||
.unwrap_err()
|
||||
.contains("participant")
|
||||
);
|
||||
assert!(state.update(&p, Event::Owner(wrong_kind)).is_err());
|
||||
assert!(state.update(&p, Event::Owner(wrong_layer)).is_err());
|
||||
state.update(&p, Event::Owner(owner)).unwrap();
|
||||
state.update(&p, Event::FirstView(first_view)).unwrap();
|
||||
let target = state.created().unwrap();
|
||||
let expected_count = p.elements.len() + 1;
|
||||
let result = roundtrip(p, &mut state);
|
||||
assert_eq!(result.elements.len(), expected_count);
|
||||
assert_eq!(result.elements[&target.behavior].parent, Some(owner));
|
||||
assert_eq!(result.elements[&target.behavior].kind(), Kind::StateMachine);
|
||||
assert_eq!(result.diagrams.len(), usize::from(first_view));
|
||||
if let Some(view) = target.diagram {
|
||||
let diagram = &result.diagrams[&view];
|
||||
assert_eq!(
|
||||
diagram.projection,
|
||||
ProjectionKind::StateMachine {
|
||||
machine: target.behavior
|
||||
}
|
||||
);
|
||||
assert_eq!(diagram.nodes.len(), 1);
|
||||
assert_eq!(
|
||||
diagram.nodes.values().next().unwrap().element,
|
||||
target.behavior
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_and_illegal_family_layer_switches_never_create_model_objects() {
|
||||
let p = Project::empty();
|
||||
let mut state = start(&p, Layer::Oa, Family::Scenario, "Raw name");
|
||||
state
|
||||
.update(
|
||||
&p,
|
||||
Event::Viewpoint(ScenarioViewpoint::ParticipantInteraction),
|
||||
)
|
||||
.unwrap();
|
||||
let snapshot = state.recovery_snapshot().unwrap();
|
||||
assert!(state.update(&p, Event::Open(Layer::Sa)).is_err());
|
||||
assert!(state.update(&p, Event::Choose(Family::Sequence)).is_err());
|
||||
assert_eq!(state.recovery_snapshot().unwrap(), snapshot);
|
||||
assert!(state.update(&p, Event::Cancel).unwrap().is_none());
|
||||
assert!(!state.has_pending());
|
||||
assert!(state.created().is_none());
|
||||
assert!(state.prepare_pending(&p).is_err());
|
||||
assert!(p.elements.is_empty());
|
||||
assert!(p.diagrams.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recovery_preserves_exact_raw_inputs_reserved_ids_and_revalidates_stale_owner() {
|
||||
let mut p = Project::empty();
|
||||
let owner = p
|
||||
.add_element(Kind::Entity, Layer::Oa, "Plant", None)
|
||||
.unwrap();
|
||||
let mut state = start(&p, Layer::Oa, Family::Machine, " \nRaw rejected name\0 ");
|
||||
state.update(&p, Event::Owner(owner)).unwrap();
|
||||
state.update(&p, Event::FirstView(true)).unwrap();
|
||||
state
|
||||
.update(&p, Event::Search(Picker::Owner, " Plant ".into()))
|
||||
.unwrap();
|
||||
let raw = state.recovery_snapshot().unwrap().unwrap();
|
||||
let mut restored = State::from_recovery(raw.clone()).unwrap();
|
||||
assert_eq!(restored.recovery_snapshot().unwrap(), Some(raw.clone()));
|
||||
assert_eq!(restored.created(), state.created());
|
||||
assert_eq!(restored.pending_layer(), Some(Layer::Oa));
|
||||
assert!(restored.prepare_pending(&p).is_err());
|
||||
restored
|
||||
.update(&p, Event::Name("Plant modes".into()))
|
||||
.unwrap();
|
||||
let command = restored.prepare_pending(&p).unwrap();
|
||||
let again = State::from_recovery(restored.recovery_snapshot().unwrap().unwrap()).unwrap();
|
||||
assert_eq!(again.prepare_pending(&p).unwrap(), command);
|
||||
p.elements.remove(&owner);
|
||||
let before = restored.recovery_snapshot().unwrap();
|
||||
assert!(
|
||||
restored
|
||||
.prepare_pending(&p)
|
||||
.unwrap_err()
|
||||
.contains("participant")
|
||||
);
|
||||
assert_eq!(restored.recovery_snapshot().unwrap(), before);
|
||||
let different = Project::empty();
|
||||
assert!(
|
||||
restored
|
||||
.prepare_pending(&different)
|
||||
.unwrap_err()
|
||||
.contains("another document")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejected_source_transaction_keeps_draft_and_every_accepted_source_byte() {
|
||||
let p = Project::empty();
|
||||
let initial = Document::from_project(p.clone(), ProjectLimits::default()).unwrap();
|
||||
let bytes: usize = initial.bundle().files.values().map(String::len).sum();
|
||||
let limits = ProjectLimits {
|
||||
model: language::ParseLimits {
|
||||
max_source_bytes: bytes + 32,
|
||||
..language::ParseLimits::UNLIMITED
|
||||
},
|
||||
..ProjectLimits::default()
|
||||
};
|
||||
let mut doc = Document::from_bundle(initial.bundle().clone(), limits).unwrap();
|
||||
let mut state = start(
|
||||
&p,
|
||||
Layer::Oa,
|
||||
Family::Sequence,
|
||||
"A long exact name retained after the source budget refuses this new process",
|
||||
);
|
||||
let raw = state.recovery_snapshot().unwrap();
|
||||
let command = state.prepare_pending(&p).unwrap();
|
||||
assert!(doc.execute(command).is_err());
|
||||
assert_eq!(doc.project(), &p);
|
||||
assert_eq!(doc.bundle(), initial.bundle());
|
||||
assert!(!doc.can_undo());
|
||||
assert_eq!(state.recovery_snapshot().unwrap(), raw);
|
||||
state.update(&p, Event::Cancel).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn owner_search_is_name_ordered_filtered_and_page_sized_not_a_catalog() {
|
||||
let mut p = Project::empty();
|
||||
for index in (0..105).rev() {
|
||||
p.add_element(Kind::Entity, Layer::Oa, &format!("Pump {index:03}"), None)
|
||||
.unwrap();
|
||||
}
|
||||
p.add_element(Kind::System, Layer::Sa, "Pump 999", None)
|
||||
.unwrap();
|
||||
p.add_element(Kind::Activity, Layer::Oa, "Pump work", None)
|
||||
.unwrap();
|
||||
let mut state = start(&p, Layer::Oa, Family::Machine, "Pump modes");
|
||||
let all = choices(&p, Layer::Oa, Picker::Owner, "");
|
||||
assert_eq!(all.len(), 105);
|
||||
assert_eq!(all[0].name, "Pump 000");
|
||||
state
|
||||
.update(&p, Event::Page(Picker::Owner, usize::MAX))
|
||||
.unwrap();
|
||||
assert!(
|
||||
state.view(&p).is_some(),
|
||||
"stale pages are clamped before slicing"
|
||||
);
|
||||
state
|
||||
.update(&p, Event::Search(Picker::Owner, "Pump 104".into()))
|
||||
.unwrap();
|
||||
assert_eq!(state.draft.as_ref().unwrap().browsers[0].page, 0);
|
||||
let found = choices(&p, Layer::Oa, Picker::Owner, "Pump 104");
|
||||
assert_eq!(found.len(), 1);
|
||||
state.update(&p, Event::Owner(found[0].id)).unwrap();
|
||||
assert_eq!(state.draft.as_ref().unwrap().owner, Some(found[0].id));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeated_owner_names_remain_distinguishable_by_searchable_parent_context_and_alias() {
|
||||
let mut p = Project::empty();
|
||||
let north = p
|
||||
.add_element(Kind::Entity, Layer::Oa, "North station", None)
|
||||
.unwrap();
|
||||
let south = p
|
||||
.add_element(Kind::Entity, Layer::Oa, "South station", None)
|
||||
.unwrap();
|
||||
let first = p
|
||||
.add_element(Kind::Entity, Layer::Oa, "Pump", Some(north))
|
||||
.unwrap();
|
||||
let second = p
|
||||
.add_element(Kind::Entity, Layer::Oa, "Pump", Some(south))
|
||||
.unwrap();
|
||||
let label = reference_label(&p, second);
|
||||
assert_ne!(reference_label(&p, first), label);
|
||||
assert!(label.contains("South station") && label.contains("Pump"));
|
||||
assert!(label.contains(&p.elements[&second].alias));
|
||||
let found = choices(&p, Layer::Oa, Picker::Owner, "south station");
|
||||
assert!(found.iter().any(|e| e.id == second));
|
||||
assert!(!found.iter().any(|e| e.id == first));
|
||||
let mut state = start(&p, Layer::Oa, Family::Machine, "Pump states");
|
||||
state.update(&p, Event::Owner(second)).unwrap();
|
||||
let result = execute_batch(&p, &[state.prepare_pending(&p).unwrap()]).unwrap();
|
||||
assert_eq!(
|
||||
result.elements[&state.created().unwrap().behavior].parent,
|
||||
Some(second)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn corrupted_recovery_and_reserved_identity_collision_refuse_without_repairing_raw_values() {
|
||||
let p = Project::empty();
|
||||
let state = start(&p, Layer::Sa, Family::Sequence, "Supply");
|
||||
let raw = state.recovery_snapshot().unwrap().unwrap();
|
||||
for (field, value) in [
|
||||
("version", serde_json::json!(99)),
|
||||
("unexpected", serde_json::json!(true)),
|
||||
] {
|
||||
let mut bad = raw.clone();
|
||||
bad[field] = value;
|
||||
assert!(State::from_recovery(bad).is_err());
|
||||
}
|
||||
let mut bad = raw;
|
||||
bad["element"] = serde_json::json!(p.id);
|
||||
let restored = State::from_recovery(bad.clone()).unwrap();
|
||||
assert!(
|
||||
restored
|
||||
.prepare_pending(&p)
|
||||
.unwrap_err()
|
||||
.contains("identity")
|
||||
);
|
||||
assert_eq!(restored.recovery_snapshot().unwrap(), Some(bad));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn post_commit_selection_is_correlated_with_exact_created_behavior_and_view() {
|
||||
let mut p = Project::empty();
|
||||
let owner = p
|
||||
.add_element(Kind::Entity, Layer::Oa, "Pump", None)
|
||||
.unwrap();
|
||||
let mut state = start(&p, Layer::Oa, Family::Machine, "Pump modes");
|
||||
state.update(&p, Event::Owner(owner)).unwrap();
|
||||
state.update(&p, Event::FirstView(true)).unwrap();
|
||||
let command = state.prepare_pending(&p).unwrap();
|
||||
assert_eq!(state.created_by(&p, &command), state.created());
|
||||
assert_eq!(
|
||||
state.created_by(&p, &Command::Rename(owner, "Unrelated change".into())),
|
||||
None
|
||||
);
|
||||
for edit in 0..4 {
|
||||
let mut changed = command.clone();
|
||||
let Command::Batch(ref mut commands) = changed else {
|
||||
panic!("creation batch");
|
||||
};
|
||||
if edit < 3 {
|
||||
let Command::CreateElement(element) = &mut commands[0] else {
|
||||
panic!("new behavior");
|
||||
};
|
||||
match edit {
|
||||
0 => element.id = Id::new_v4(),
|
||||
1 => element.name = "A different authored draft".into(),
|
||||
_ => element.parent = None,
|
||||
}
|
||||
} else {
|
||||
let Command::CreateDiagram(diagram) = &mut commands[1] else {
|
||||
panic!("new view");
|
||||
};
|
||||
diagram.projection = ProjectionKind::StateMachine { machine: owner };
|
||||
}
|
||||
assert_eq!(state.created_by(&p, &changed), None);
|
||||
}
|
||||
let Command::Batch(commands) = command else {
|
||||
panic!("creation batch");
|
||||
};
|
||||
assert_eq!(
|
||||
state.created_by(&p, &commands[0]),
|
||||
None,
|
||||
"an opt-in view must be part of the actual committed batch"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optional_context_collapses_without_losing_authored_reference_or_raw_search() {
|
||||
let mut p = Project::empty();
|
||||
let context = p
|
||||
.add_element(Kind::Capability, Layer::Oa, "Water supply", None)
|
||||
.unwrap();
|
||||
let mut state = start(&p, Layer::Oa, Family::Scenario, "Supply nominal");
|
||||
assert!(!state.draft.as_ref().unwrap().context_open);
|
||||
state
|
||||
.update(&p, Event::Viewpoint(ScenarioViewpoint::OperationalActivity))
|
||||
.unwrap();
|
||||
state.update(&p, Event::ContextOptions(true)).unwrap();
|
||||
state
|
||||
.update(&p, Event::Search(Picker::Context, " Water ".into()))
|
||||
.unwrap();
|
||||
state.update(&p, Event::Context(Some(context))).unwrap();
|
||||
let command = state.prepare_pending(&p).unwrap();
|
||||
state.update(&p, Event::ContextOptions(false)).unwrap();
|
||||
let restored = State::from_recovery(state.recovery_snapshot().unwrap().unwrap()).unwrap();
|
||||
assert!(!restored.draft.as_ref().unwrap().context_open);
|
||||
assert_eq!(
|
||||
restored.draft.as_ref().unwrap().browsers[1].query,
|
||||
" Water "
|
||||
);
|
||||
assert_eq!(restored.prepare_pending(&p).unwrap(), command);
|
||||
assert_eq!(restored.created_by(&p, &command), restored.created());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn creation_selection_rejects_changed_scenario_body_and_post_creation_operations() {
|
||||
let mut p = Project::empty();
|
||||
let capability = p
|
||||
.add_element(Kind::SystemCapability, Layer::Sa, "Supply", None)
|
||||
.unwrap();
|
||||
let mut state = start(&p, Layer::Sa, Family::Scenario, "Nominal functions");
|
||||
state
|
||||
.update(&p, Event::Viewpoint(ScenarioViewpoint::Functional))
|
||||
.unwrap();
|
||||
let command = state.prepare_pending(&p).unwrap();
|
||||
assert_eq!(state.created_by(&p, &command), state.created());
|
||||
for edit in 0..4 {
|
||||
let mut altered = command.clone();
|
||||
let Command::Batch(commands) = &mut altered else {
|
||||
panic!("creation batch");
|
||||
};
|
||||
match edit {
|
||||
0 => {
|
||||
let Command::CreateElement(element) = &mut commands[0] else {
|
||||
panic!("scenario");
|
||||
};
|
||||
let Some(SharedBody::Scenario { viewpoint, .. }) = element.shared_mut() else {
|
||||
panic!("scenario body");
|
||||
};
|
||||
*viewpoint = ScenarioViewpoint::BoundaryInterface;
|
||||
}
|
||||
1 => {
|
||||
let Command::CreateElement(element) = &mut commands[0] else {
|
||||
panic!("scenario");
|
||||
};
|
||||
let Some(SharedBody::Scenario { context, .. }) = element.shared_mut() else {
|
||||
panic!("scenario body");
|
||||
};
|
||||
*context = Some(capability);
|
||||
}
|
||||
2 => commands.push(Command::Rename(
|
||||
state.created().unwrap().behavior,
|
||||
"Changed afterward".into(),
|
||||
)),
|
||||
_ => commands.push(Command::Delete(state.created().unwrap().behavior)),
|
||||
}
|
||||
assert!(
|
||||
execute_batch(&p, std::slice::from_ref(&altered)).is_ok(),
|
||||
"the alternate command is valid, but is not this authored creation"
|
||||
);
|
||||
assert_eq!(state.created_by(&p, &altered), None);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ use super::topology::{occurrence, unique_batch};
|
||||
use super::*;
|
||||
use iced::widget::{Column, column, text_input};
|
||||
use std::{cell::RefCell, collections::BTreeSet};
|
||||
mod workspace;
|
||||
pub use workspace::{Shelf, view};
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "scenario_operation_tests.rs"]
|
||||
@@ -92,8 +94,13 @@ impl std::fmt::Display for CanvasTool {
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CanvasEvent {
|
||||
First(Context, Tool),
|
||||
Scrolled(Context, ScrollArea, iced::Vector),
|
||||
Restored(Context, Id, ScrollArea, iced::Vector),
|
||||
Viewport(Context, Size),
|
||||
Zoom(Context, Option<f32>),
|
||||
Focus(Context),
|
||||
Shelf(Context, Shelf),
|
||||
OpenUse(Context, Id),
|
||||
Back(Context),
|
||||
Choose(Context, CanvasTool),
|
||||
@@ -120,13 +127,19 @@ pub enum CanvasEvent {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum ScrollArea {
|
||||
Canvas,
|
||||
// Retained navigation channel, now the independent Selection/outline shelf.
|
||||
Page,
|
||||
}
|
||||
impl CanvasEvent {
|
||||
pub fn context(&self) -> Context {
|
||||
match self {
|
||||
Self::Scrolled(context, _, _)
|
||||
Self::First(context, _)
|
||||
| Self::Scrolled(context, _, _)
|
||||
| Self::Restored(context, ..)
|
||||
| Self::Viewport(context, _)
|
||||
| Self::Zoom(context, _)
|
||||
| Self::Focus(context)
|
||||
| Self::Shelf(context, _)
|
||||
| Self::OpenUse(context, _)
|
||||
| Self::Back(context)
|
||||
| Self::Choose(context, _)
|
||||
@@ -151,6 +164,7 @@ impl std::fmt::Display for Operator {
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Event {
|
||||
Source(super::source_draft::Event),
|
||||
Start(Id, Tool),
|
||||
Cancel,
|
||||
Submit,
|
||||
@@ -191,6 +205,7 @@ pub struct State {
|
||||
scroll_offsets: [iced::Vector; 2],
|
||||
return_to: Vec<Caller>,
|
||||
restore: Option<ViewportRestore>,
|
||||
presentation: workspace::Presentation,
|
||||
}
|
||||
const CALLER_LIMIT: usize = 32;
|
||||
pub(crate) struct Caller {
|
||||
@@ -200,6 +215,7 @@ pub(crate) struct Caller {
|
||||
target: Id,
|
||||
tool: CanvasTool,
|
||||
offsets: [iced::Vector; 2],
|
||||
presentation: workspace::Presentation,
|
||||
}
|
||||
struct ViewportRestore {
|
||||
document: Id,
|
||||
@@ -227,6 +243,13 @@ struct CallChoiceContext {
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct Draft {
|
||||
#[serde(default)]
|
||||
source: super::source_draft::State,
|
||||
#[serde(default = "Id::new_v4")]
|
||||
invocation: Id,
|
||||
#[serde(default)]
|
||||
source_origin: Option<(Id, oa_core::Element)>,
|
||||
|
||||
owner: Id,
|
||||
parent: Id,
|
||||
before: Option<Id>,
|
||||
@@ -259,6 +282,54 @@ struct Draft {
|
||||
choice_page: usize,
|
||||
}
|
||||
impl State {
|
||||
pub(super) fn pending_name(&self) -> Option<&str> {
|
||||
self.draft.as_ref().map(|d| d.name.as_str())
|
||||
}
|
||||
pub(super) fn source_view<'a>(&'a self, p: &'a Project) -> Option<iced::Element<'a, Event>> {
|
||||
self.draft
|
||||
.as_ref()?
|
||||
.source
|
||||
.view(p)
|
||||
.map(|v| v.map(Event::Source))
|
||||
}
|
||||
|
||||
pub fn source_active(&self) -> bool {
|
||||
self.draft.as_ref().is_some_and(|d| d.source.engaged())
|
||||
}
|
||||
|
||||
pub fn source_open(&self) -> bool {
|
||||
self.draft.as_ref().is_some_and(|d| d.source.is_open())
|
||||
}
|
||||
pub fn source_back(&mut self) -> bool {
|
||||
self.draft.as_mut().is_some_and(|d| d.source.back())
|
||||
}
|
||||
|
||||
pub(crate) fn enter(&mut self, document: Id, owner: Id) {
|
||||
if self.presentation.identity != Some((document, owner)) {
|
||||
self.presentation = workspace::Presentation {
|
||||
identity: Some((document, owner)),
|
||||
focused: self.presentation.focused,
|
||||
..Default::default()
|
||||
};
|
||||
self.scroll_offsets = [iced::Vector::ZERO; 2];
|
||||
self.restore = None;
|
||||
}
|
||||
}
|
||||
pub(crate) fn viewport(&mut self, size: Size) -> bool {
|
||||
self.presentation.viewport(size)
|
||||
}
|
||||
pub(crate) fn zoom(&mut self, scale: Option<f32>) -> bool {
|
||||
self.presentation.zoom(scale)
|
||||
}
|
||||
pub(crate) fn focused(&self) -> bool {
|
||||
self.presentation.focused
|
||||
}
|
||||
pub(crate) fn toggle_focus(&mut self) {
|
||||
self.presentation.focused = !self.presentation.focused;
|
||||
}
|
||||
pub(crate) fn toggle_shelf(&mut self, shelf: Shelf) {
|
||||
self.presentation.shelf = (self.presentation.shelf != Some(shelf)).then_some(shelf);
|
||||
}
|
||||
pub(crate) fn open_use(
|
||||
&mut self,
|
||||
p: &Project,
|
||||
@@ -279,6 +350,7 @@ impl State {
|
||||
target,
|
||||
tool: self.canvas_tool(context.scenario),
|
||||
offsets: self.scroll_offsets,
|
||||
presentation: self.presentation.clone(),
|
||||
});
|
||||
self.canvas_tool = Some((target, CanvasTool::Select));
|
||||
self.scroll_offsets = [iced::Vector::ZERO; 2];
|
||||
@@ -309,6 +381,8 @@ impl State {
|
||||
}
|
||||
self.canvas_tool = Some((frame.owner, frame.tool));
|
||||
self.scroll_offsets = frame.offsets;
|
||||
self.presentation = frame.presentation;
|
||||
self.presentation.identity = Some((context.document, context.scenario));
|
||||
self.restore = Some(ViewportRestore {
|
||||
document: context.document,
|
||||
owner: frame.owner,
|
||||
@@ -378,6 +452,9 @@ impl State {
|
||||
}
|
||||
|
||||
pub(crate) fn scrolled(&mut self, area: ScrollArea, offset: iced::Vector) -> bool {
|
||||
if !offset.x.is_finite() || !offset.y.is_finite() || offset.x < 0. || offset.y < 0. {
|
||||
return false;
|
||||
}
|
||||
let index = match area {
|
||||
ScrollArea::Canvas => 0,
|
||||
ScrollArea::Page => 1,
|
||||
@@ -387,7 +464,8 @@ impl State {
|
||||
changed
|
||||
}
|
||||
fn scroll_offset(&self) -> iced::Vector {
|
||||
self.scroll_offsets[0] + self.scroll_offsets[1]
|
||||
// The former page scroll now belongs to a sibling outline shelf.
|
||||
self.scroll_offsets[0]
|
||||
}
|
||||
fn call_choices(&self, p: &Project, context: Context, draft: &Draft) -> Vec<Choice> {
|
||||
let key = CallChoiceContext {
|
||||
@@ -406,8 +484,16 @@ impl State {
|
||||
}
|
||||
cached.as_ref().unwrap().1.clone()
|
||||
}
|
||||
pub(crate) fn created_selection(&self, command: &Command) -> Option<Id> {
|
||||
pub(crate) fn created_selection(&self, p: &Project, command: &Command) -> Option<Id> {
|
||||
let draft = self.draft.as_ref()?;
|
||||
if self
|
||||
.draft
|
||||
.as_ref()
|
||||
.is_some_and(|d| d.source.label(d.target).is_some())
|
||||
&& self.prepare_pending(p).ok().as_ref() != Some(command)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
fn created(command: &Command, parent: Id) -> Option<Id> {
|
||||
match command {
|
||||
Command::CreateElement(element) if element.parent == Some(parent) => {
|
||||
@@ -522,6 +608,61 @@ impl State {
|
||||
}
|
||||
|
||||
pub fn update(&mut self, p: &Project, event: Event) -> Result<Option<Command>, String> {
|
||||
if let Event::Source(event) = event {
|
||||
if self
|
||||
.draft
|
||||
.as_mut()
|
||||
.ok_or("Start the consumer form first.")?
|
||||
.source
|
||||
.dismiss(&event)?
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
let d = self
|
||||
.draft
|
||||
.as_ref()
|
||||
.ok_or("Start the consumer form first.")?;
|
||||
if d.tool != Tool::Lifeline {
|
||||
return Err("This reference does not support staged source creation.".into());
|
||||
}
|
||||
if d.source_origin.as_ref().is_some_and(|(document, owner)| {
|
||||
*document != p.id || p.elements.get(&d.owner) != Some(owner)
|
||||
}) {
|
||||
return Err("The original consumer changed. Cancel and reopen its form; the current draft is retained.".into());
|
||||
}
|
||||
if d.source_origin.is_none() && d.source.engaged() {
|
||||
return Err("The recovered source draft has no original document binding. Go Back or cancel it.".into());
|
||||
}
|
||||
let mut next = d.clone();
|
||||
if next.source_origin.is_none() && matches!(&event, super::source_draft::Event::Open(_))
|
||||
{
|
||||
next.source_origin = Some((
|
||||
p.id,
|
||||
p.elements
|
||||
.get(&d.owner)
|
||||
.ok_or("The consumer owner no longer exists.")?
|
||||
.clone(),
|
||||
));
|
||||
}
|
||||
if let Some(id) = next.source.update(
|
||||
p,
|
||||
super::source_draft::Scope::Lifeline {
|
||||
owner: d.owner,
|
||||
viewpoint: viewpoint(p, d.owner),
|
||||
},
|
||||
d.invocation,
|
||||
event,
|
||||
)? {
|
||||
next.target = Some(id);
|
||||
next.command(p)?;
|
||||
}
|
||||
self.draft = Some(next);
|
||||
return Ok(None);
|
||||
}
|
||||
if self.source_open() && !matches!(&event, Event::Cancel) {
|
||||
return Err("Use the new source or go Back before editing the parent form.".into());
|
||||
}
|
||||
|
||||
if self.draft.is_some() && matches!(&event, Event::Start(..)) {
|
||||
return Err(PENDING_DRAFT.into());
|
||||
}
|
||||
@@ -531,6 +672,9 @@ impl State {
|
||||
return Err("Choose a scenario.".into());
|
||||
}
|
||||
self.draft = Some(Draft {
|
||||
source: Default::default(),
|
||||
invocation: Id::new_v4(),
|
||||
source_origin: Some((p.id, p.elements[&owner].clone())),
|
||||
owner,
|
||||
parent: owner,
|
||||
before: None,
|
||||
@@ -894,7 +1038,14 @@ fn valid_body_choice(p: &Project, owner: Id, body: OccurrenceBody) -> bool {
|
||||
execute_batch(p, &[Command::CreateElement(e)]).is_ok()
|
||||
}
|
||||
fn sources(p: &Project, d: &Draft) -> Vec<Choice> {
|
||||
let layer = p.elements[&d.owner].layer();
|
||||
let Some(owner) = p
|
||||
.elements
|
||||
.get(&d.owner)
|
||||
.filter(|e| e.kind() == Kind::Scenario)
|
||||
else {
|
||||
return vec![];
|
||||
};
|
||||
let layer = owner.layer();
|
||||
let mut values: Vec<_> = p
|
||||
.elements
|
||||
.values()
|
||||
@@ -1210,6 +1361,28 @@ impl Draft {
|
||||
e
|
||||
}
|
||||
fn command(&self, p: &Project) -> Result<Command, String> {
|
||||
if self.source.engaged()
|
||||
&& self.source_origin.as_ref().is_none_or(|(document, owner)| {
|
||||
*document != p.id || p.elements.get(&self.owner) != Some(owner)
|
||||
})
|
||||
{
|
||||
return Err("The original consumer document or owner changed. Cancel and reopen its form; your draft is retained.".into());
|
||||
}
|
||||
self.source.check_scope(
|
||||
super::source_draft::Scope::Lifeline {
|
||||
owner: self.owner,
|
||||
viewpoint: viewpoint(p, self.owner),
|
||||
},
|
||||
self.target,
|
||||
)?;
|
||||
let preview = self.source.preview(p, self.target)?;
|
||||
let command = self.consumer_command(preview.as_ref().unwrap_or(p))?;
|
||||
self.source.combine(p, self.target, command)
|
||||
}
|
||||
fn consumer_command(&self, p: &Project) -> Result<Command, String> {
|
||||
if p.elements.get(&self.owner).map(oa_core::Element::kind) != Some(Kind::Scenario) {
|
||||
return Err("The scenario no longer exists. Cancel or restore its owner.".into());
|
||||
}
|
||||
if !parents(p, self.owner).iter().any(|c| c.id == self.parent) {
|
||||
return Err("Choose this scenario or one of its operands as destination.".into());
|
||||
}
|
||||
@@ -1220,6 +1393,8 @@ impl Draft {
|
||||
.unwrap_or(self.tool.label());
|
||||
let name = if self.name.trim().is_empty() {
|
||||
default
|
||||
} else if self.source.label(self.target).is_some() {
|
||||
self.name.as_str()
|
||||
} else {
|
||||
self.name.trim()
|
||||
};
|
||||
@@ -1634,493 +1809,6 @@ fn bounded_picker<'a>(
|
||||
}
|
||||
out.into()
|
||||
}
|
||||
pub fn view<'a>(app: &'a App, owner: Id, mut out: Column<'a, Message>) -> Element<'a, Message> {
|
||||
let p = app.project();
|
||||
out = out.push(label(match viewpoint(p, owner) {
|
||||
ScenarioViewpoint::OperationalActivity => {
|
||||
"Operational activity scenario · lifelines represent activities"
|
||||
}
|
||||
ScenarioViewpoint::Functional => {
|
||||
"Functional scenario · lifelines represent system functions"
|
||||
}
|
||||
ScenarioViewpoint::ParticipantInteraction => {
|
||||
"Participant scenario · lifelines represent entities, actors or roles"
|
||||
}
|
||||
ScenarioViewpoint::BoundaryInterface => {
|
||||
"Interface scenario · lifelines represent the system and external actors"
|
||||
}
|
||||
ScenarioViewpoint::MixedLegacy => {
|
||||
"Mixed scenario · lifelines may represent work or participants"
|
||||
}
|
||||
}));
|
||||
let pending = app.behavior.has_pending();
|
||||
let context = app.scenario_context().expect("visible scenario context");
|
||||
let tool = app.behavior.scenario.canvas_tool(owner);
|
||||
// Keep the canvas at a stable widget-tree position when a form replaces
|
||||
// the palette, so Iced retains the user's current sequence viewport.
|
||||
let mut controls = column![].spacing(6);
|
||||
if !pending {
|
||||
controls = controls.push(
|
||||
row![
|
||||
button("Select / reorder")
|
||||
.on_press(Message::ScenarioCanvas(CanvasEvent::Choose(
|
||||
context,
|
||||
CanvasTool::Select
|
||||
)))
|
||||
.style(if tool == CanvasTool::Select {
|
||||
style::selected
|
||||
} else {
|
||||
style::nav
|
||||
}),
|
||||
pick_list(
|
||||
vec![
|
||||
CanvasTool::Select,
|
||||
CanvasTool::Message(MessageStyle::CallReply),
|
||||
CanvasTool::Message(MessageStyle::Asynchronous),
|
||||
CanvasTool::Message(MessageStyle::Call),
|
||||
CanvasTool::Message(MessageStyle::Reply),
|
||||
CanvasTool::Message(MessageStyle::Create),
|
||||
CanvasTool::Message(MessageStyle::Delete),
|
||||
CanvasTool::Message(MessageStyle::Timer),
|
||||
CanvasTool::Add(Tool::Action),
|
||||
CanvasTool::Add(Tool::Observation),
|
||||
CanvasTool::Add(Tool::Reuse),
|
||||
CanvasTool::Add(Tool::Fragment),
|
||||
],
|
||||
Some(tool),
|
||||
move |tool| Message::ScenarioCanvas(CanvasEvent::Choose(context, tool))
|
||||
)
|
||||
.text_size(12)
|
||||
.padding(8)
|
||||
.width(240),
|
||||
action("+ Lifeline", event(Event::Start(owner, Tool::Lifeline))),
|
||||
action("Properties", Message::EditProperties(owner)),
|
||||
]
|
||||
.spacing(6)
|
||||
.wrap(),
|
||||
);
|
||||
controls = controls.push(label(match tool {
|
||||
CanvasTool::Message(_) => "Draw message · choose a sequence position on a lifeline, then click or drag to another lifeline · Escape cancels",
|
||||
CanvasTool::Add(Tool::Action | Tool::Observation) => "Click a highlighted sequence position · click on a lifeline to preselect it · Escape cancels",
|
||||
CanvasTool::Add(Tool::Reuse) => "Click a highlighted sequence position to reuse a scenario and bind its lifelines · Escape cancels",
|
||||
CanvasTool::Add(Tool::Fragment) => "Click a highlighted sequence position to insert an empty fragment with operands · Escape cancels",
|
||||
_ => "Select a lifeline or occurrence · drag its ↕ handle to reorder within its sequence",
|
||||
}));
|
||||
} else {
|
||||
controls = controls.push(label(
|
||||
"Pending form · the canvas remains visible · apply or cancel below",
|
||||
));
|
||||
}
|
||||
let clean = !pending && !app.draft_dirty() && !app.source_dirty();
|
||||
let mut navigation = row![].spacing(6);
|
||||
if referenced_scenario(p, owner, app.selected).is_some() {
|
||||
navigation = navigation.push(button("Open definition").on_press_maybe(clean.then_some(
|
||||
Message::ScenarioCanvas(CanvasEvent::OpenUse(context, app.selected)),
|
||||
)));
|
||||
}
|
||||
if app.behavior.scenario.can_back(p, context) {
|
||||
navigation =
|
||||
navigation.push(button("Back to caller").on_press_maybe(
|
||||
clean.then_some(Message::ScenarioCanvas(CanvasEvent::Back(context))),
|
||||
));
|
||||
}
|
||||
controls = controls.push(navigation.wrap());
|
||||
out = out.push(controls);
|
||||
let mut scene = Scene::sequence(p, owner, app.selected);
|
||||
scene.interaction = Some((
|
||||
context,
|
||||
tool,
|
||||
!app.behavior.has_pending() && !app.draft_dirty() && !app.source_dirty(),
|
||||
));
|
||||
scene.scroll_offset = app.behavior.scenario.scroll_offset();
|
||||
scene.pending_placement = app.behavior.scenario.pending_placement(owner);
|
||||
let size = scene.size;
|
||||
out = out.push(
|
||||
container(super::scenario_scroll::observe(
|
||||
context,
|
||||
ScrollArea::Canvas,
|
||||
app.behavior
|
||||
.scenario
|
||||
.scroll_restore(context, ScrollArea::Canvas),
|
||||
scrollable(
|
||||
canvas::Canvas::new(scene)
|
||||
.width(size.width)
|
||||
.height(size.height),
|
||||
)
|
||||
.direction(scrollable::Direction::Both {
|
||||
vertical: scrollable::Scrollbar::default(),
|
||||
horizontal: scrollable::Scrollbar::default(),
|
||||
})
|
||||
.height(if pending {
|
||||
(app.window_size.height - 660.).clamp(110., 190.)
|
||||
} else {
|
||||
(app.window_size.height - 410.).clamp(180., 340.)
|
||||
}),
|
||||
))
|
||||
.padding(8)
|
||||
.style(style::card),
|
||||
);
|
||||
if let Some(d) = app
|
||||
.behavior
|
||||
.scenario
|
||||
.draft
|
||||
.as_ref()
|
||||
.filter(|d| d.owner == owner)
|
||||
{
|
||||
let mut form = column![
|
||||
text(d.tool.label()).size(16),
|
||||
text_input("Name (defaults to selected source)", &d.name)
|
||||
.on_input(|s| event(Event::Name(s)))
|
||||
.padding(9)
|
||||
.size(12)
|
||||
]
|
||||
.spacing(9);
|
||||
if d.tool != Tool::Lifeline {
|
||||
form = form.push(label(format!(
|
||||
"Insert: {}",
|
||||
slot_label(
|
||||
p,
|
||||
Slot {
|
||||
parent: d.parent,
|
||||
before: d.before
|
||||
}
|
||||
)
|
||||
)));
|
||||
if d.before.is_some() {
|
||||
form = form.push(
|
||||
button("Append to this sequence instead").on_press(event(Event::AppendInstead)),
|
||||
);
|
||||
}
|
||||
form = form.push(picker(
|
||||
parents(p, owner),
|
||||
Some(d.parent),
|
||||
"Destination sequence / operand…",
|
||||
Event::Parent,
|
||||
));
|
||||
}
|
||||
if matches!(
|
||||
d.tool,
|
||||
Tool::Lifeline | Tool::Reuse | Tool::Action | Tool::Observation
|
||||
) {
|
||||
form = form.push(picker(
|
||||
sources(p, d),
|
||||
d.target,
|
||||
"Choose a named source…",
|
||||
Event::Target,
|
||||
));
|
||||
}
|
||||
if d.tool == Tool::Message {
|
||||
form = form.push(
|
||||
pick_list(
|
||||
vec![
|
||||
MessageStyle::Asynchronous,
|
||||
MessageStyle::Call,
|
||||
MessageStyle::CallReply,
|
||||
MessageStyle::Reply,
|
||||
MessageStyle::Create,
|
||||
MessageStyle::Delete,
|
||||
MessageStyle::Timer,
|
||||
],
|
||||
Some(d.style),
|
||||
|style| event(Event::Style(style)),
|
||||
)
|
||||
.width(Fill)
|
||||
.padding(9)
|
||||
.text_size(12),
|
||||
);
|
||||
if d.style == MessageStyle::Reply {
|
||||
form = form.push(bounded_picker(
|
||||
app.behavior.scenario.call_choices(p, context, d),
|
||||
d,
|
||||
d.call,
|
||||
"Choose the earlier call…",
|
||||
Event::Call,
|
||||
));
|
||||
if d.call.is_some() {
|
||||
form = form.push(label(
|
||||
"Reply reuses the call exchange in reverse and retains its correlation.",
|
||||
));
|
||||
}
|
||||
} else if d.style != MessageStyle::Timer {
|
||||
form = form.push(bounded_picker(
|
||||
exchanges(p, d),
|
||||
d,
|
||||
d.exchange,
|
||||
"Choose the exchange…",
|
||||
Event::Exchange,
|
||||
));
|
||||
form = form.push(label(
|
||||
"Apply checks that the message is valid at this insertion position.",
|
||||
));
|
||||
}
|
||||
form = form.push(picker(
|
||||
d.line_choices(p, true),
|
||||
d.sender,
|
||||
"Sending lifeline…",
|
||||
Event::Sender,
|
||||
));
|
||||
if d.style != MessageStyle::Timer {
|
||||
form = form.push(picker(
|
||||
d.line_choices(p, false),
|
||||
d.receiver,
|
||||
"Receiving lifeline…",
|
||||
Event::Receiver,
|
||||
));
|
||||
}
|
||||
if d.style == MessageStyle::Timer {
|
||||
form = form.push(
|
||||
text_input("Duration / deadline, e.g. 30 s", &d.expression)
|
||||
.on_input(|s| event(Event::Expression(s)))
|
||||
.padding(9)
|
||||
.size(12),
|
||||
);
|
||||
}
|
||||
if p.layer_of(owner) == Some(Layer::Sa)
|
||||
&& viewpoint(p, owner) == ScenarioViewpoint::BoundaryInterface
|
||||
&& matches!(
|
||||
d.style,
|
||||
MessageStyle::Call | MessageStyle::CallReply | MessageStyle::Asynchronous
|
||||
)
|
||||
{
|
||||
form = form.push(
|
||||
iced::widget::checkbox(d.operation.is_some())
|
||||
.label("Invoke a specific interface operation")
|
||||
.on_toggle(|enabled| event(Event::OperationIntent(enabled))),
|
||||
);
|
||||
if let Some(operation) = &d.operation {
|
||||
form = form
|
||||
.push(bounded_picker(
|
||||
crate::data_values::operation_choices(p, &d.proposed_message(p)),
|
||||
d,
|
||||
operation.allocation,
|
||||
"Choose the exact interface operation allocation…",
|
||||
Event::Operation,
|
||||
))
|
||||
.push(
|
||||
button("Keep operation unresolved")
|
||||
.on_press(event(Event::ClearOperation)),
|
||||
);
|
||||
if let Some(id) = operation.allocation {
|
||||
form = form.push(label(crate::data_values::operation_identity(p, id)));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(description) =
|
||||
crate::data_values::operation_label(p, &d.proposed_message(p))
|
||||
{
|
||||
form = form.push(label(description));
|
||||
}
|
||||
match d.signature(p) {
|
||||
Ok(Some(exchange)) => form = form.push(value_panel(p, d, &exchange)),
|
||||
Err(problem) => {
|
||||
form = form.push(label(problem));
|
||||
for (key, expression) in d.authored_values() {
|
||||
form = form
|
||||
.push(
|
||||
text(format!(
|
||||
"Retained value · {} / {}",
|
||||
key.path
|
||||
.iter()
|
||||
.map(|id| p.named(*id))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" → "),
|
||||
p.named(key.field)
|
||||
))
|
||||
.size(11),
|
||||
)
|
||||
.push(
|
||||
text_input("Value expression", expression)
|
||||
.on_input({
|
||||
let key = key.clone();
|
||||
move |raw| event(Event::Value(key.clone(), raw))
|
||||
})
|
||||
.padding(8)
|
||||
.size(12),
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(None) => {}
|
||||
}
|
||||
if d.style == MessageStyle::CallReply {
|
||||
form=form.push(label("Creates the call and reversed, correlated reply together in one undoable operation."));
|
||||
}
|
||||
}
|
||||
if matches!(d.tool, Tool::Action | Tool::Observation) {
|
||||
form = form.push(picker(
|
||||
lines(p, owner),
|
||||
d.sender,
|
||||
"Observed lifeline…",
|
||||
Event::Sender,
|
||||
));
|
||||
}
|
||||
if d.tool == Tool::Reuse
|
||||
&& let Some(target) = d.target
|
||||
{
|
||||
for line in lines(p, target) {
|
||||
let referenced = line.id;
|
||||
let compatible: Vec<_> = lines(p, owner)
|
||||
.into_iter()
|
||||
.filter(|c| {
|
||||
compatible_binding(p, referenced, c.id)
|
||||
&& !d
|
||||
.bindings
|
||||
.iter()
|
||||
.any(|(r, l)| *r != referenced && *l == c.id)
|
||||
})
|
||||
.collect();
|
||||
if compatible.is_empty() {
|
||||
form = form.push(label(format!(
|
||||
"Add a compatible local lifeline before binding '{}'.",
|
||||
p.named(referenced)
|
||||
)));
|
||||
}
|
||||
form = form
|
||||
.push(label(format!("Bind {}", p.named(referenced))))
|
||||
.push(picker(
|
||||
compatible,
|
||||
d.bindings.get(&referenced).copied(),
|
||||
"Compatible local lifeline…",
|
||||
move |c| Event::Binding(referenced, c),
|
||||
));
|
||||
}
|
||||
form = form.push(label(
|
||||
"Bindings are explicit, one-to-one and refer to existing lifelines.",
|
||||
));
|
||||
}
|
||||
if d.tool == Tool::Fragment {
|
||||
form = form.push(
|
||||
pick_list(
|
||||
vec![
|
||||
Operator(FragmentOperator::Alternative),
|
||||
Operator(FragmentOperator::Parallel),
|
||||
Operator(FragmentOperator::Loop),
|
||||
Operator(FragmentOperator::Optional),
|
||||
],
|
||||
Some(Operator(d.operator)),
|
||||
|o| event(Event::Operator(o)),
|
||||
)
|
||||
.padding(9)
|
||||
.text_size(12)
|
||||
.width(Fill),
|
||||
);
|
||||
if d.operator != FragmentOperator::Parallel {
|
||||
form = form.push(
|
||||
text_input("Condition / loop guard", &d.expression)
|
||||
.on_input(|s| event(Event::Expression(s)))
|
||||
.padding(9)
|
||||
.size(12),
|
||||
);
|
||||
}
|
||||
if d.operator == FragmentOperator::Alternative {
|
||||
form = form.push(
|
||||
text_input("Second alternative guard", &d.second_guard)
|
||||
.on_input(|s| event(Event::SecondGuard(s)))
|
||||
.padding(9)
|
||||
.size(12),
|
||||
);
|
||||
}
|
||||
form=form.push(label("Creates the fragment with its operands. Choose an operand as destination when adding messages or actions."));
|
||||
}
|
||||
let readiness = d.command(p);
|
||||
if let Err(error) = &readiness {
|
||||
form = form.push(label(error.clone()));
|
||||
}
|
||||
let footer = row![
|
||||
button("Create")
|
||||
.padding(9)
|
||||
.on_press_maybe(readiness.is_ok().then_some(event(Event::Submit))),
|
||||
action("Cancel", event(Event::Cancel))
|
||||
]
|
||||
.spacing(7);
|
||||
out = out.push(
|
||||
container(
|
||||
column![
|
||||
scrollable(form).height((app.window_size.height - 650.).clamp(110., 240.)),
|
||||
footer
|
||||
]
|
||||
.spacing(9),
|
||||
)
|
||||
.padding(14)
|
||||
.style(style::card),
|
||||
);
|
||||
}
|
||||
if lines(p, owner).is_empty() && p.elements[&owner].steps().is_none_or(Vec::is_empty) {
|
||||
out = out.push(container(label("Add lifelines for the participating work or actors, then create messages between them.")).padding(16).width(Fill).style(style::card));
|
||||
} else if !pending {
|
||||
out = sequence_rows(p, context, owner, out);
|
||||
}
|
||||
super::scenario_scroll::observe(
|
||||
context,
|
||||
ScrollArea::Page,
|
||||
app.behavior
|
||||
.scenario
|
||||
.scroll_restore(context, ScrollArea::Page),
|
||||
scrollable(out.padding(24)),
|
||||
)
|
||||
}
|
||||
fn sequence_rows<'a>(
|
||||
p: &'a Project,
|
||||
context: Context,
|
||||
owner: Id,
|
||||
mut out: Column<'a, Message>,
|
||||
) -> Column<'a, Message> {
|
||||
if let Some(SharedBody::Occurrence {
|
||||
body: OccurrenceBody::Fragment { branches, .. },
|
||||
..
|
||||
}) = p.elements.get(&owner).and_then(|e| e.shared())
|
||||
{
|
||||
for branch in branches {
|
||||
out = out.push(action(
|
||||
&format!("Operand: {}", p.named(*branch)),
|
||||
Message::Select(*branch),
|
||||
));
|
||||
out = sequence_rows(p, context, *branch, out);
|
||||
}
|
||||
}
|
||||
if let Some(steps) = p.elements.get(&owner).and_then(|e| e.steps()) {
|
||||
for (i, id) in steps.iter().enumerate() {
|
||||
out =
|
||||
out.push(
|
||||
row![
|
||||
action(
|
||||
&format!("{}. {}", i + 1, p.named(*id)),
|
||||
Message::Select(*id)
|
||||
)
|
||||
.width(Fill),
|
||||
button("↑")
|
||||
.padding(8)
|
||||
.on_press_maybe((i > 0).then_some(Message::Command(
|
||||
Command::MoveStep(owner, i, i.saturating_sub(1))
|
||||
))),
|
||||
button("↓").padding(8).on_press_maybe(
|
||||
(i + 1 < steps.len()).then_some(Message::Command(Command::MoveStep(
|
||||
owner,
|
||||
i,
|
||||
i + 1
|
||||
)))
|
||||
)
|
||||
]
|
||||
.spacing(6),
|
||||
);
|
||||
if let Some(SharedBody::Occurrence {
|
||||
body:
|
||||
OccurrenceBody::ScenarioUse {
|
||||
scenario: Some(target),
|
||||
..
|
||||
},
|
||||
..
|
||||
}) = p.elements[id].shared()
|
||||
{
|
||||
out = out.push(action(
|
||||
&format!("Open {} →", p.named(*target)),
|
||||
Message::ScenarioCanvas(CanvasEvent::OpenUse(context, *id)),
|
||||
));
|
||||
}
|
||||
out = sequence_rows(p, context, *id, out);
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod direct_tests {
|
||||
@@ -2180,7 +1868,7 @@ pub(crate) mod direct_tests {
|
||||
epoch: 0,
|
||||
}
|
||||
}
|
||||
fn placement_fixture() -> (Fixture, Id, Id, Id, Id, Id) {
|
||||
pub(super) fn placement_fixture() -> (Fixture, Id, Id, Id, Id, Id) {
|
||||
let mut f = fixture();
|
||||
let p = &mut f.project;
|
||||
let machine = p
|
||||
@@ -2334,7 +2022,12 @@ pub(crate) mod direct_tests {
|
||||
);
|
||||
assert_eq!(
|
||||
app.behavior.scenario.scroll_offset(),
|
||||
iced::Vector::new(25., 250.)
|
||||
iced::Vector::new(25., 170.)
|
||||
);
|
||||
assert_eq!(
|
||||
app.behavior.scenario.scroll_offsets[1],
|
||||
iced::Vector::new(0., 80.),
|
||||
"The caller's independent outline scroll is restored separately"
|
||||
);
|
||||
assert!(
|
||||
!app.behavior
|
||||
@@ -2471,6 +2164,11 @@ pub(crate) mod direct_tests {
|
||||
assert!(app.document.can_undo(), "{}", app.status);
|
||||
navigation_event(&mut app, CanvasEvent::Back);
|
||||
assert_eq!(app.selected, if edit == 0 { first } else { b });
|
||||
assert_eq!(
|
||||
app.scenario_context().unwrap().scenario,
|
||||
if edit == 0 { a } else { b },
|
||||
"Back must restore the caller workspace even if a rename already selected its UUID"
|
||||
);
|
||||
assert_eq!(app.project(), &project);
|
||||
assert_eq!(app.document.bundle(), &source);
|
||||
assert_eq!(app.document.revision(), revision);
|
||||
@@ -2909,7 +2607,7 @@ pub(crate) mod direct_tests {
|
||||
assert!(app.busy);
|
||||
assert_eq!(
|
||||
app.behavior.scenario.scroll_offset(),
|
||||
iced::Vector::new(25., 140.)
|
||||
iced::Vector::new(25., 100.)
|
||||
);
|
||||
assert_ne!(
|
||||
app.scenario_context(),
|
||||
@@ -2925,7 +2623,7 @@ pub(crate) mod direct_tests {
|
||||
)));
|
||||
assert_eq!(
|
||||
app.behavior.scenario.scroll_offset(),
|
||||
iced::Vector::new(25., 140.)
|
||||
iced::Vector::new(25., 100.)
|
||||
);
|
||||
let root = std::path::PathBuf::from("scenario-scroll-save-fixture");
|
||||
let receipt = crate::app::FileSnapshot {
|
||||
|
||||
@@ -0,0 +1,809 @@
|
||||
//! Scenario presentation. Layout/camera controls never construct model commands.
|
||||
use super::super::scenario_scroll;
|
||||
use super::*;
|
||||
use iced::widget::{Space, column, sensor};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Shelf {
|
||||
Tools,
|
||||
Selection,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub(super) struct Presentation {
|
||||
pub(super) identity: Option<(Id, Id)>,
|
||||
pub(super) viewport: Option<Size>,
|
||||
pub(super) focused: bool,
|
||||
pub(super) shelf: Option<Shelf>,
|
||||
pub(super) zoom: Option<f32>,
|
||||
pub(super) fit: bool,
|
||||
}
|
||||
const MIN_SCALE: f32 = 0.05;
|
||||
impl Presentation {
|
||||
pub(super) fn viewport(&mut self, size: Size) -> bool {
|
||||
if !size.width.is_finite()
|
||||
|| !size.height.is_finite()
|
||||
|| size.width <= 0.
|
||||
|| size.height <= 0.
|
||||
|| self.viewport == Some(size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
self.viewport = Some(size);
|
||||
true
|
||||
}
|
||||
pub(super) fn zoom(&mut self, scale: Option<f32>) -> bool {
|
||||
if scale.is_some_and(|value| !value.is_finite() || value <= 0.) {
|
||||
return false;
|
||||
}
|
||||
let next = scale.map(|value| value.clamp(MIN_SCALE, 3.));
|
||||
let changed = self.zoom != next || self.fit != scale.is_none();
|
||||
self.zoom = next;
|
||||
self.fit = scale.is_none();
|
||||
changed
|
||||
}
|
||||
fn fit_scale(&self, scene: Size) -> Option<f32> {
|
||||
let viewport = self.viewport?;
|
||||
// This is the measured inner Scrollable, never a window-height estimate.
|
||||
// Leave room for the overlay scrollbars on both axes.
|
||||
Some(
|
||||
((viewport.width - 16.).max(1.) / scene.width.max(1.))
|
||||
.min((viewport.height - 16.).max(1.) / scene.height.max(1.)),
|
||||
)
|
||||
}
|
||||
fn scale(&self, scene: Size) -> f32 {
|
||||
if self.fit {
|
||||
self.fit_scale(scene).unwrap_or(1.).clamp(MIN_SCALE, 1.)
|
||||
} else {
|
||||
self.zoom.unwrap_or(1.)
|
||||
}
|
||||
}
|
||||
fn fit_limited(&self, scene: Size) -> bool {
|
||||
self.fit && self.fit_scale(scene).is_some_and(|scale| scale < MIN_SCALE)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view<'a>(app: &'a App, owner: Id, out: Column<'a, Message>) -> Element<'a, Message> {
|
||||
let p = app.project();
|
||||
let state = &app.behavior.scenario;
|
||||
let presentation = &state.presentation;
|
||||
let pending = state
|
||||
.draft
|
||||
.as_ref()
|
||||
.is_some_and(|draft| draft.owner == owner);
|
||||
let context = app.scenario_context().expect("visible scenario context");
|
||||
let tool = state.canvas_tool(owner);
|
||||
let clean = !app.behavior.has_pending() && !app.draft_dirty() && !app.source_dirty();
|
||||
let mut scene = Scene::sequence(p, owner, app.selected);
|
||||
let scale = presentation.scale(scene.size);
|
||||
let limited = presentation.fit_limited(scene.size);
|
||||
scene.scale = scale;
|
||||
scene.interaction = Some((context, tool, clean));
|
||||
// The outline is a sibling shelf, not an ancestor of this canvas.
|
||||
scene.scroll_offset = state.scroll_offset();
|
||||
scene.pending_placement = state.pending_placement(owner);
|
||||
let size = Size::new(scene.size.width * scale, scene.size.height * scale);
|
||||
let canvas = canvas_pane(
|
||||
context,
|
||||
state.scroll_restore(context, ScrollArea::Canvas),
|
||||
canvas::Canvas::new(scene)
|
||||
.width(size.width)
|
||||
.height(size.height)
|
||||
.into(),
|
||||
);
|
||||
let mut navigation = row![
|
||||
out.width(Fill),
|
||||
button(
|
||||
text(if presentation.focused {
|
||||
"Restore panels"
|
||||
} else {
|
||||
"Focus canvas"
|
||||
})
|
||||
.size(12)
|
||||
)
|
||||
.padding(6)
|
||||
.on_press(Message::ScenarioCanvas(CanvasEvent::Focus(context)))
|
||||
]
|
||||
.spacing(6)
|
||||
.align_y(iced::Center);
|
||||
if state.can_back(p, context) {
|
||||
navigation = navigation.push(
|
||||
button(text("Back to caller").size(12))
|
||||
.padding(6)
|
||||
.on_press_maybe(
|
||||
clean.then_some(Message::ScenarioCanvas(CanvasEvent::Back(context))),
|
||||
),
|
||||
);
|
||||
}
|
||||
let mut header = column![
|
||||
navigation,
|
||||
row![
|
||||
button(text("Tools").size(12))
|
||||
.padding(6)
|
||||
.style(if presentation.shelf == Some(Shelf::Tools) {
|
||||
button::primary
|
||||
} else {
|
||||
style::nav
|
||||
})
|
||||
.on_press(Message::ScenarioCanvas(CanvasEvent::Shelf(
|
||||
context,
|
||||
Shelf::Tools
|
||||
))),
|
||||
button(text("Selection").size(12))
|
||||
.padding(6)
|
||||
.style(if presentation.shelf == Some(Shelf::Selection) {
|
||||
button::primary
|
||||
} else {
|
||||
style::nav
|
||||
})
|
||||
.on_press(Message::ScenarioCanvas(CanvasEvent::Shelf(
|
||||
context,
|
||||
Shelf::Selection
|
||||
))),
|
||||
button(text("Fit overview").size(12))
|
||||
.padding(6)
|
||||
.on_press(Message::ScenarioCanvas(CanvasEvent::Zoom(context, None))),
|
||||
button(text("−").size(12))
|
||||
.padding(6)
|
||||
.on_press(Message::ScenarioCanvas(CanvasEvent::Zoom(
|
||||
context,
|
||||
Some(scale / 1.25)
|
||||
))),
|
||||
text(format!("{:.0}%", scale * 100.)).size(12),
|
||||
button(text("+").size(12))
|
||||
.padding(6)
|
||||
.on_press(Message::ScenarioCanvas(CanvasEvent::Zoom(
|
||||
context,
|
||||
Some(scale * 1.25)
|
||||
))),
|
||||
button(text("100%").size(12))
|
||||
.padding(6)
|
||||
.on_press(Message::ScenarioCanvas(CanvasEvent::Zoom(
|
||||
context,
|
||||
Some(1.)
|
||||
)))
|
||||
]
|
||||
.spacing(6)
|
||||
.align_y(iced::Center)
|
||||
.wrap(),
|
||||
text(if pending {
|
||||
"Pending form · the highlighted sequence position is retained".to_owned()
|
||||
} else if !p.children(owner).any(|e| e.kind() == Kind::Lifeline) {
|
||||
"Start with a lifeline representing existing or explicitly new work / participants."
|
||||
.into()
|
||||
} else if !p.children(owner).any(|e| e.kind() == Kind::Occurrence) {
|
||||
"Choose a first message, action or another tool; no occurrence is selected yet.".into()
|
||||
} else {
|
||||
let guidance = match tool {
|
||||
CanvasTool::Message(_) => {
|
||||
"Click a source position, then click or drag to a target · Esc cancels"
|
||||
}
|
||||
CanvasTool::Add(Tool::Action | Tool::Observation) => {
|
||||
"Click a highlighted position · a lifeline click preselects that lifeline"
|
||||
}
|
||||
CanvasTool::Add(Tool::Reuse | Tool::Fragment) => {
|
||||
"Click a highlighted sequence position to open its form"
|
||||
}
|
||||
_ => "Select a lifeline or occurrence · drag its ↕ handle to reorder",
|
||||
};
|
||||
format!("{tool} · {guidance}")
|
||||
})
|
||||
.size(11)
|
||||
]
|
||||
.spacing(6);
|
||||
if !pending {
|
||||
if !p.children(owner).any(|e| e.kind() == Kind::Lifeline) {
|
||||
header = header.push(
|
||||
row![
|
||||
text(format!("No lifelines · {:?}", viewpoint(p, owner))).size(12),
|
||||
button("Add first lifeline").on_press_maybe(clean.then_some({
|
||||
Message::ScenarioCanvas(CanvasEvent::First(context, Tool::Lifeline))
|
||||
}))
|
||||
]
|
||||
.spacing(8),
|
||||
);
|
||||
} else if !p.children(owner).any(|e| e.kind() == Kind::Occurrence) {
|
||||
header = header.push(
|
||||
row![
|
||||
text("No occurrences yet").size(12),
|
||||
button("Add first message").on_press_maybe(clean.then_some({
|
||||
Message::ScenarioCanvas(CanvasEvent::First(context, Tool::Message))
|
||||
})),
|
||||
button("Add first action").on_press_maybe(clean.then_some({
|
||||
Message::ScenarioCanvas(CanvasEvent::First(context, Tool::Action))
|
||||
}))
|
||||
]
|
||||
.spacing(8),
|
||||
);
|
||||
}
|
||||
}
|
||||
if limited {
|
||||
header = header
|
||||
.push(text("Minimum zoom reached · scroll to see the remaining scenario.").size(11));
|
||||
}
|
||||
let details = match presentation.shelf {
|
||||
Some(Shelf::Tools) => {
|
||||
let choose = move |tool| Message::ScenarioCanvas(CanvasEvent::Choose(context, tool));
|
||||
let tool_picker: Element<'_, Message> = if clean {
|
||||
pick_list(
|
||||
vec![
|
||||
CanvasTool::Select,
|
||||
CanvasTool::Message(MessageStyle::CallReply),
|
||||
CanvasTool::Message(MessageStyle::Asynchronous),
|
||||
CanvasTool::Message(MessageStyle::Call),
|
||||
CanvasTool::Message(MessageStyle::Reply),
|
||||
CanvasTool::Message(MessageStyle::Create),
|
||||
CanvasTool::Message(MessageStyle::Delete),
|
||||
CanvasTool::Message(MessageStyle::Timer),
|
||||
CanvasTool::Add(Tool::Action),
|
||||
CanvasTool::Add(Tool::Observation),
|
||||
CanvasTool::Add(Tool::Reuse),
|
||||
CanvasTool::Add(Tool::Fragment),
|
||||
],
|
||||
Some(tool),
|
||||
choose,
|
||||
)
|
||||
.text_size(12)
|
||||
.padding(6)
|
||||
.width(Fill)
|
||||
.into()
|
||||
} else {
|
||||
button(text(format!("{tool} · finish pending edits to change tools")).size(12))
|
||||
.padding(6)
|
||||
.width(Fill)
|
||||
.into()
|
||||
};
|
||||
column![
|
||||
tool_picker,
|
||||
row![
|
||||
button(text("+ Lifeline").size(12))
|
||||
.padding(6)
|
||||
.on_press_maybe(
|
||||
clean.then_some(event(Event::Start(owner, Tool::Lifeline)))
|
||||
),
|
||||
button(text("Scenario properties").size(12))
|
||||
.padding(6)
|
||||
.on_press_maybe(clean.then_some(Message::EditProperties(owner))),
|
||||
]
|
||||
.spacing(6)
|
||||
.wrap(),
|
||||
text(match viewpoint(p, owner) {
|
||||
ScenarioViewpoint::OperationalActivity =>
|
||||
"Activity scenario · lifelines represent activities",
|
||||
ScenarioViewpoint::Functional =>
|
||||
"Functional scenario · lifelines represent system functions",
|
||||
ScenarioViewpoint::ParticipantInteraction =>
|
||||
"Participant scenario · entities, actors or roles",
|
||||
ScenarioViewpoint::BoundaryInterface =>
|
||||
"Interface scenario · system and external actors",
|
||||
ScenarioViewpoint::MixedLegacy => "Mixed scenario · work or participants",
|
||||
})
|
||||
.size(11),
|
||||
]
|
||||
.spacing(6)
|
||||
}
|
||||
_ => {
|
||||
let mut details =
|
||||
column![text(format!("Selected: {}", p.named(app.selected))).size(12)].spacing(6);
|
||||
let mut navigation = row![].spacing(6);
|
||||
if referenced_scenario(p, owner, app.selected).is_some() {
|
||||
navigation = navigation.push(
|
||||
button(text("Open definition").size(12))
|
||||
.padding(6)
|
||||
.on_press_maybe(clean.then_some(Message::ScenarioCanvas(
|
||||
CanvasEvent::OpenUse(context, app.selected),
|
||||
))),
|
||||
);
|
||||
}
|
||||
navigation = navigation.push(
|
||||
button(text("Properties").size(12))
|
||||
.padding(6)
|
||||
.on_press_maybe(clean.then_some(Message::EditProperties(app.selected))),
|
||||
);
|
||||
details = details.push(navigation.wrap());
|
||||
if lines(p, owner).is_empty() && p.elements[&owner].steps().is_none_or(Vec::is_empty) {
|
||||
details.push(
|
||||
text("Use Tools to add lifelines, then draw messages or insert occurrences.")
|
||||
.size(12),
|
||||
)
|
||||
} else {
|
||||
sequence_rows(p, context, owner, details, clean)
|
||||
}
|
||||
}
|
||||
};
|
||||
let details = details_pane(
|
||||
context,
|
||||
state.scroll_restore(context, ScrollArea::Page),
|
||||
details.into(),
|
||||
presentation.shelf.is_some(),
|
||||
pending,
|
||||
);
|
||||
let mut form_panel = editor_panel(Space::new().height(0).into(), Space::new().height(0).into());
|
||||
if let Some(d) = app
|
||||
.behavior
|
||||
.scenario
|
||||
.draft
|
||||
.as_ref()
|
||||
.filter(|d| d.owner == owner)
|
||||
{
|
||||
if let Some(child) = d.source.view(p) {
|
||||
form_panel = column![
|
||||
text(format!(
|
||||
"Consumer name: {}",
|
||||
crate::views::short(&d.name, 60)
|
||||
))
|
||||
.size(12),
|
||||
child.map(|e| event(Event::Source(e)))
|
||||
]
|
||||
.spacing(6)
|
||||
.height(Fill)
|
||||
.into();
|
||||
} else {
|
||||
let mut form = column![
|
||||
text(d.tool.label()).size(16),
|
||||
text_input("Name (defaults to selected source)", &d.name)
|
||||
.on_input(|s| event(Event::Name(s)))
|
||||
.padding(9)
|
||||
.size(12)
|
||||
]
|
||||
.spacing(9);
|
||||
if d.tool != Tool::Lifeline {
|
||||
form = form.push(label(format!(
|
||||
"Insert: {}",
|
||||
slot_label(
|
||||
p,
|
||||
Slot {
|
||||
parent: d.parent,
|
||||
before: d.before
|
||||
}
|
||||
)
|
||||
)));
|
||||
if d.before.is_some() {
|
||||
form = form.push(
|
||||
button("Append to this sequence instead")
|
||||
.on_press(event(Event::AppendInstead)),
|
||||
);
|
||||
}
|
||||
form = form.push(picker(
|
||||
parents(p, owner),
|
||||
Some(d.parent),
|
||||
"Destination sequence / operand…",
|
||||
Event::Parent,
|
||||
));
|
||||
}
|
||||
if matches!(
|
||||
d.tool,
|
||||
Tool::Lifeline | Tool::Reuse | Tool::Action | Tool::Observation
|
||||
) {
|
||||
form = form.push(picker(
|
||||
sources(p, d),
|
||||
d.target,
|
||||
"Choose a named source…",
|
||||
Event::Target,
|
||||
));
|
||||
}
|
||||
if d.tool == Tool::Lifeline {
|
||||
if let Some(title) = d.source.label(d.target) {
|
||||
form = form.push(label(title)).push(label(
|
||||
"Create commits one new source and this lifeline together.",
|
||||
));
|
||||
}
|
||||
if viewpoint(p, owner) != ScenarioViewpoint::MixedLegacy {
|
||||
form = form.push(button("Create new source…").on_press(event(Event::Source(
|
||||
super::super::source_draft::Event::Open(d.invocation),
|
||||
))));
|
||||
} else {
|
||||
form=form.push(label("New source creation requires a strict viewpoint. Existing sources remain available."));
|
||||
}
|
||||
}
|
||||
if d.tool == Tool::Message {
|
||||
form = form.push(
|
||||
pick_list(
|
||||
vec![
|
||||
MessageStyle::Asynchronous,
|
||||
MessageStyle::Call,
|
||||
MessageStyle::CallReply,
|
||||
MessageStyle::Reply,
|
||||
MessageStyle::Create,
|
||||
MessageStyle::Delete,
|
||||
MessageStyle::Timer,
|
||||
],
|
||||
Some(d.style),
|
||||
|style| event(Event::Style(style)),
|
||||
)
|
||||
.width(Fill)
|
||||
.padding(9)
|
||||
.text_size(12),
|
||||
);
|
||||
if d.style == MessageStyle::Reply {
|
||||
form = form.push(bounded_picker(
|
||||
app.behavior.scenario.call_choices(p, context, d),
|
||||
d,
|
||||
d.call,
|
||||
"Choose the earlier call…",
|
||||
Event::Call,
|
||||
));
|
||||
if d.call.is_some() {
|
||||
form = form.push(label(
|
||||
"Reply reuses the call exchange in reverse and retains its correlation.",
|
||||
));
|
||||
}
|
||||
} else if d.style != MessageStyle::Timer {
|
||||
form = form.push(bounded_picker(
|
||||
exchanges(p, d),
|
||||
d,
|
||||
d.exchange,
|
||||
"Choose the exchange…",
|
||||
Event::Exchange,
|
||||
));
|
||||
form = form.push(label(
|
||||
"Apply checks that the message is valid at this insertion position.",
|
||||
));
|
||||
}
|
||||
form = form.push(picker(
|
||||
d.line_choices(p, true),
|
||||
d.sender,
|
||||
"Sending lifeline…",
|
||||
Event::Sender,
|
||||
));
|
||||
if d.style != MessageStyle::Timer {
|
||||
form = form.push(picker(
|
||||
d.line_choices(p, false),
|
||||
d.receiver,
|
||||
"Receiving lifeline…",
|
||||
Event::Receiver,
|
||||
));
|
||||
}
|
||||
if d.style == MessageStyle::Timer {
|
||||
form = form.push(
|
||||
text_input("Duration / deadline, e.g. 30 s", &d.expression)
|
||||
.on_input(|s| event(Event::Expression(s)))
|
||||
.padding(9)
|
||||
.size(12),
|
||||
);
|
||||
}
|
||||
if p.layer_of(owner) == Some(Layer::Sa)
|
||||
&& viewpoint(p, owner) == ScenarioViewpoint::BoundaryInterface
|
||||
&& matches!(
|
||||
d.style,
|
||||
MessageStyle::Call | MessageStyle::CallReply | MessageStyle::Asynchronous
|
||||
)
|
||||
{
|
||||
form = form.push(
|
||||
iced::widget::checkbox(d.operation.is_some())
|
||||
.label("Invoke a specific interface operation")
|
||||
.on_toggle(|enabled| event(Event::OperationIntent(enabled))),
|
||||
);
|
||||
if let Some(operation) = &d.operation {
|
||||
form = form
|
||||
.push(bounded_picker(
|
||||
crate::data_values::operation_choices(p, &d.proposed_message(p)),
|
||||
d,
|
||||
operation.allocation,
|
||||
"Choose the exact interface operation allocation…",
|
||||
Event::Operation,
|
||||
))
|
||||
.push(
|
||||
button("Keep operation unresolved")
|
||||
.on_press(event(Event::ClearOperation)),
|
||||
);
|
||||
if let Some(id) = operation.allocation {
|
||||
form = form.push(label(crate::data_values::operation_identity(p, id)));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(description) =
|
||||
crate::data_values::operation_label(p, &d.proposed_message(p))
|
||||
{
|
||||
form = form.push(label(description));
|
||||
}
|
||||
match d.signature(p) {
|
||||
Ok(Some(exchange)) => form = form.push(value_panel(p, d, &exchange)),
|
||||
Err(problem) => {
|
||||
form = form.push(label(problem));
|
||||
for (key, expression) in d.authored_values() {
|
||||
form = form
|
||||
.push(
|
||||
text(format!(
|
||||
"Retained value · {} / {}",
|
||||
key.path
|
||||
.iter()
|
||||
.map(|id| p.named(*id))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" → "),
|
||||
p.named(key.field)
|
||||
))
|
||||
.size(11),
|
||||
)
|
||||
.push(
|
||||
text_input("Value expression", expression)
|
||||
.on_input({
|
||||
let key = key.clone();
|
||||
move |raw| event(Event::Value(key.clone(), raw))
|
||||
})
|
||||
.padding(8)
|
||||
.size(12),
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(None) => {}
|
||||
}
|
||||
if d.style == MessageStyle::CallReply {
|
||||
form=form.push(label("Creates the call and reversed, correlated reply together in one undoable operation."));
|
||||
}
|
||||
}
|
||||
if matches!(d.tool, Tool::Action | Tool::Observation) {
|
||||
form = form.push(picker(
|
||||
lines(p, owner),
|
||||
d.sender,
|
||||
"Observed lifeline…",
|
||||
Event::Sender,
|
||||
));
|
||||
}
|
||||
if d.tool == Tool::Reuse
|
||||
&& let Some(target) = d.target
|
||||
{
|
||||
for line in lines(p, target) {
|
||||
let referenced = line.id;
|
||||
let compatible: Vec<_> = lines(p, owner)
|
||||
.into_iter()
|
||||
.filter(|c| {
|
||||
compatible_binding(p, referenced, c.id)
|
||||
&& !d
|
||||
.bindings
|
||||
.iter()
|
||||
.any(|(r, l)| *r != referenced && *l == c.id)
|
||||
})
|
||||
.collect();
|
||||
if compatible.is_empty() {
|
||||
form = form.push(label(format!(
|
||||
"Add a compatible local lifeline before binding '{}'.",
|
||||
p.named(referenced)
|
||||
)));
|
||||
}
|
||||
form = form
|
||||
.push(label(format!("Bind {}", p.named(referenced))))
|
||||
.push(picker(
|
||||
compatible,
|
||||
d.bindings.get(&referenced).copied(),
|
||||
"Compatible local lifeline…",
|
||||
move |c| Event::Binding(referenced, c),
|
||||
));
|
||||
}
|
||||
form = form.push(label(
|
||||
"Bindings are explicit, one-to-one and refer to existing lifelines.",
|
||||
));
|
||||
}
|
||||
if d.tool == Tool::Fragment {
|
||||
form = form.push(
|
||||
pick_list(
|
||||
vec![
|
||||
Operator(FragmentOperator::Alternative),
|
||||
Operator(FragmentOperator::Parallel),
|
||||
Operator(FragmentOperator::Loop),
|
||||
Operator(FragmentOperator::Optional),
|
||||
],
|
||||
Some(Operator(d.operator)),
|
||||
|o| event(Event::Operator(o)),
|
||||
)
|
||||
.padding(9)
|
||||
.text_size(12)
|
||||
.width(Fill),
|
||||
);
|
||||
if d.operator != FragmentOperator::Parallel {
|
||||
form = form.push(
|
||||
text_input("Condition / loop guard", &d.expression)
|
||||
.on_input(|s| event(Event::Expression(s)))
|
||||
.padding(9)
|
||||
.size(12),
|
||||
);
|
||||
}
|
||||
if d.operator == FragmentOperator::Alternative {
|
||||
form = form.push(
|
||||
text_input("Second alternative guard", &d.second_guard)
|
||||
.on_input(|s| event(Event::SecondGuard(s)))
|
||||
.padding(9)
|
||||
.size(12),
|
||||
);
|
||||
}
|
||||
form=form.push(label("Creates the fragment with its operands. Choose an operand as destination when adding messages or actions."));
|
||||
}
|
||||
let readiness = d.command(p);
|
||||
if let Err(error) = &readiness {
|
||||
form = form.push(label(error.clone()));
|
||||
}
|
||||
let footer = row![
|
||||
button("Create")
|
||||
.padding(9)
|
||||
.on_press_maybe(readiness.is_ok().then_some(event(Event::Submit))),
|
||||
action("Cancel", event(Event::Cancel))
|
||||
]
|
||||
.spacing(7);
|
||||
form_panel = editor_panel(footer.into(), form.into());
|
||||
}
|
||||
}
|
||||
workspace_layout(header.into(), details, canvas, form_panel, pending)
|
||||
}
|
||||
|
||||
fn sequence_action<'a>(
|
||||
title: &str,
|
||||
message: Message,
|
||||
enabled: bool,
|
||||
) -> iced::widget::Button<'a, Message> {
|
||||
button(text(title.to_owned()).size(12))
|
||||
.padding(8)
|
||||
.style(style::nav)
|
||||
.on_press_maybe(enabled.then_some(message))
|
||||
}
|
||||
|
||||
// Permanent header/shelf/body and canvas/form slots. Zero-sized disclosures
|
||||
// must remain in the tree: Column::push otherwise drops them and remounts siblings.
|
||||
fn workspace_layout<'a, R: iced::advanced::text::Renderer + 'a>(
|
||||
header: iced::Element<'a, Message, Theme, R>,
|
||||
details: iced::Element<'a, Message, Theme, R>,
|
||||
canvas: iced::Element<'a, Message, Theme, R>,
|
||||
form: iced::Element<'a, Message, Theme, R>,
|
||||
pending: bool,
|
||||
) -> iced::Element<'a, Message, Theme, R> {
|
||||
let panes = Column::from_vec(vec![
|
||||
container(canvas).width(Fill).height(Fill).into(),
|
||||
container(form)
|
||||
.width(Fill)
|
||||
.height(if pending {
|
||||
Fill
|
||||
} else {
|
||||
iced::Length::Fixed(0.)
|
||||
})
|
||||
.clip(true)
|
||||
.into(),
|
||||
])
|
||||
.spacing(if pending { 6 } else { 0 })
|
||||
.width(Fill)
|
||||
.height(Fill);
|
||||
Column::from_vec(vec![header, details, panes.into()])
|
||||
.spacing(6)
|
||||
.padding(8)
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn details_pane<'a, R: iced::advanced::text::Renderer + 'a>(
|
||||
context: Context,
|
||||
restore: Option<ScrollRestore>,
|
||||
details: iced::Element<'a, Message, Theme, R>,
|
||||
open: bool,
|
||||
pending: bool,
|
||||
) -> iced::Element<'a, Message, Theme, R> {
|
||||
scenario_scroll::observe(
|
||||
context,
|
||||
ScrollArea::Page,
|
||||
restore,
|
||||
scrollable(details)
|
||||
.id("scenario-details")
|
||||
.width(Fill)
|
||||
.height(if open {
|
||||
if pending { 72. } else { 100. }
|
||||
} else {
|
||||
0.
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn canvas_pane<'a, R: iced::advanced::text::Renderer + 'a>(
|
||||
context: Context,
|
||||
restore: Option<ScrollRestore>,
|
||||
content: iced::Element<'a, Message, Theme, R>,
|
||||
) -> iced::Element<'a, Message, Theme, R> {
|
||||
container(
|
||||
sensor(scenario_scroll::observe(
|
||||
context,
|
||||
ScrollArea::Canvas,
|
||||
restore,
|
||||
scrollable(content)
|
||||
.id("scenario-canvas")
|
||||
.direction(scrollable::Direction::Both {
|
||||
vertical: scrollable::Scrollbar::default(),
|
||||
horizontal: scrollable::Scrollbar::default(),
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill),
|
||||
))
|
||||
.key((context.document, context.scenario))
|
||||
.on_show(move |size| Message::ScenarioCanvas(CanvasEvent::Viewport(context, size)))
|
||||
.on_resize(move |size| Message::ScenarioCanvas(CanvasEvent::Viewport(context, size))),
|
||||
)
|
||||
.padding(8)
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.style(style::card)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn editor_panel<'a, R: iced::advanced::text::Renderer + 'a>(
|
||||
actions: iced::Element<'a, Message, Theme, R>,
|
||||
fields: iced::Element<'a, Message, Theme, R>,
|
||||
) -> iced::Element<'a, Message, Theme, R> {
|
||||
container(
|
||||
column![
|
||||
container(actions).id("scenario-actions").width(Fill),
|
||||
scrollable(fields)
|
||||
.id("scenario-form")
|
||||
.width(Fill)
|
||||
.height(Fill),
|
||||
]
|
||||
.spacing(6),
|
||||
)
|
||||
.padding(10)
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.style(style::card)
|
||||
.into()
|
||||
}
|
||||
|
||||
#[cfg(all(test, debug_assertions))]
|
||||
mod tests;
|
||||
|
||||
fn sequence_rows<'a>(
|
||||
p: &'a Project,
|
||||
context: Context,
|
||||
owner: Id,
|
||||
mut out: Column<'a, Message>,
|
||||
enabled: bool,
|
||||
) -> Column<'a, Message> {
|
||||
if let Some(SharedBody::Occurrence {
|
||||
body: OccurrenceBody::Fragment { branches, .. },
|
||||
..
|
||||
}) = p.elements.get(&owner).and_then(|e| e.shared())
|
||||
{
|
||||
for branch in branches {
|
||||
out = out.push(sequence_action(
|
||||
&format!("Operand: {}", p.named(*branch)),
|
||||
Message::Select(*branch),
|
||||
enabled,
|
||||
));
|
||||
out = sequence_rows(p, context, *branch, out, enabled);
|
||||
}
|
||||
}
|
||||
if let Some(steps) = p.elements.get(&owner).and_then(|e| e.steps()) {
|
||||
for (i, id) in steps.iter().enumerate() {
|
||||
out = out.push(
|
||||
row![
|
||||
sequence_action(
|
||||
&format!("{}. {}", i + 1, p.named(*id)),
|
||||
Message::Select(*id),
|
||||
enabled
|
||||
)
|
||||
.width(Fill),
|
||||
button("↑")
|
||||
.padding(8)
|
||||
.on_press_maybe((enabled && i > 0).then_some(Message::Command(
|
||||
Command::MoveStep(owner, i, i.saturating_sub(1))
|
||||
))),
|
||||
button("↓").padding(8).on_press_maybe(
|
||||
(enabled && i + 1 < steps.len())
|
||||
.then_some(Message::Command(Command::MoveStep(owner, i, i + 1)))
|
||||
)
|
||||
]
|
||||
.spacing(6),
|
||||
);
|
||||
if let Some(SharedBody::Occurrence {
|
||||
body:
|
||||
OccurrenceBody::ScenarioUse {
|
||||
scenario: Some(target),
|
||||
..
|
||||
},
|
||||
..
|
||||
}) = p.elements[id].shared()
|
||||
{
|
||||
out = out.push(sequence_action(
|
||||
&format!("Open {} →", p.named(*target)),
|
||||
Message::ScenarioCanvas(CanvasEvent::OpenUse(context, *id)),
|
||||
enabled,
|
||||
));
|
||||
}
|
||||
out = sequence_rows(p, context, *id, out, enabled);
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
@@ -0,0 +1,887 @@
|
||||
use super::*;
|
||||
use iced::Vector;
|
||||
use iced::advanced::{Layout, Shell, layout, widget};
|
||||
|
||||
#[derive(Default)]
|
||||
struct Geometry {
|
||||
scrolls: BTreeMap<String, (Rectangle, Vector)>,
|
||||
actions: Option<Rectangle>,
|
||||
}
|
||||
impl widget::Operation for Geometry {
|
||||
fn traverse(&mut self, visit: &mut dyn FnMut(&mut dyn widget::Operation)) {
|
||||
visit(self);
|
||||
}
|
||||
fn container(&mut self, id: Option<&widget::Id>, bounds: Rectangle) {
|
||||
if id == Some(&widget::Id::new("scenario-actions")) {
|
||||
self.actions = Some(bounds);
|
||||
}
|
||||
}
|
||||
fn scrollable(
|
||||
&mut self,
|
||||
id: Option<&widget::Id>,
|
||||
bounds: Rectangle,
|
||||
_: Rectangle,
|
||||
translation: Vector,
|
||||
_: &mut dyn widget::operation::Scrollable,
|
||||
) {
|
||||
for name in ["scenario-canvas", "scenario-details", "scenario-form"] {
|
||||
if id == Some(&widget::Id::new(name)) {
|
||||
assert!(
|
||||
self.scrolls
|
||||
.insert(name.into(), (bounds, translation))
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
type TestView = iced::Element<'static, Message, Theme, ()>;
|
||||
fn fixture_view(state: &State, context: Context, pending: bool, header: f32) -> TestView {
|
||||
workspace_layout(
|
||||
Space::new().height(header).into(),
|
||||
details_pane(
|
||||
context,
|
||||
state.scroll_restore(context, ScrollArea::Page),
|
||||
Space::new().height(1800.).into(),
|
||||
state.presentation.shelf.is_some(),
|
||||
pending,
|
||||
),
|
||||
canvas_pane(
|
||||
context,
|
||||
state.scroll_restore(context, ScrollArea::Canvas),
|
||||
Space::new().width(1800.).height(2400.).into(),
|
||||
),
|
||||
editor_panel(
|
||||
Space::new().height(32.).into(),
|
||||
Space::new().height(2000.).into(),
|
||||
),
|
||||
pending,
|
||||
)
|
||||
}
|
||||
fn geometry(view: &mut TestView, tree: &mut widget::Tree, size: Size) -> (layout::Node, Geometry) {
|
||||
let node = view
|
||||
.as_widget_mut()
|
||||
.layout(tree, &(), &layout::Limits::new(Size::ZERO, size));
|
||||
let mut geometry = Geometry::default();
|
||||
view.as_widget_mut()
|
||||
.operate(tree, Layout::new(&node), &(), &mut geometry);
|
||||
(node, geometry)
|
||||
}
|
||||
fn scroll(
|
||||
view: &mut TestView,
|
||||
tree: &mut widget::Tree,
|
||||
node: &layout::Node,
|
||||
name: &'static str,
|
||||
offset: Vector,
|
||||
) {
|
||||
let mut operation = widget::operation::scrollable::scroll_to::<()>(
|
||||
widget::Id::new(name),
|
||||
widget::operation::scrollable::AbsoluteOffset {
|
||||
x: Some(offset.x),
|
||||
y: Some(offset.y),
|
||||
},
|
||||
);
|
||||
view.as_widget_mut()
|
||||
.operate(tree, Layout::new(node), &(), &mut operation);
|
||||
}
|
||||
fn redraw(view: &mut TestView, tree: &mut widget::Tree, size: Size, app: &mut App) {
|
||||
let (node, _) = geometry(view, tree, size);
|
||||
let mut messages = vec![];
|
||||
view.as_widget_mut().update(
|
||||
tree,
|
||||
&iced::Event::Window(iced::window::Event::RedrawRequested(
|
||||
std::time::Instant::now(),
|
||||
)),
|
||||
Layout::new(&node),
|
||||
mouse::Cursor::Unavailable,
|
||||
&(),
|
||||
&mut iced::advanced::clipboard::Null,
|
||||
&mut Shell::new(&mut messages),
|
||||
&Rectangle::with_size(size),
|
||||
);
|
||||
for message in messages {
|
||||
let _ = app.update(message);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scenario_workspace_allocates_remaining_height_and_pins_actions_at_minimum_and_wide_sizes() {
|
||||
for size in [
|
||||
Size::new(500., 570.),
|
||||
Size::new(1050., 570.),
|
||||
Size::new(840., 770.),
|
||||
Size::new(1394., 802.),
|
||||
] {
|
||||
for pending in [false, true] {
|
||||
for shelf in [None, Some(Shelf::Tools), Some(Shelf::Selection)] {
|
||||
for header in [68., 100., 132.] {
|
||||
let mut state = State::default();
|
||||
state.presentation.shelf = shelf;
|
||||
let mut view = fixture_view(
|
||||
&state,
|
||||
super::super::direct_tests::context(Id::new_v4()),
|
||||
pending,
|
||||
header,
|
||||
);
|
||||
let mut tree = widget::Tree::new(view.as_widget());
|
||||
let (node, actual) = geometry(&mut view, &mut tree, size);
|
||||
assert_eq!(node.size(), size);
|
||||
let canvas = actual.scrolls["scenario-canvas"].0;
|
||||
assert!(canvas.x >= 0. && canvas.y >= header);
|
||||
assert!(
|
||||
canvas.x + canvas.width <= size.width
|
||||
&& canvas.y + canvas.height <= size.height
|
||||
);
|
||||
assert!(
|
||||
canvas.height >= if pending { 145. } else { 290. },
|
||||
"{size:?} header={header} pending={pending} shelf={shelf:?}: {canvas:?}"
|
||||
);
|
||||
if pending {
|
||||
let form = actual.scrolls["scenario-form"].0;
|
||||
let actions = actual.actions.unwrap();
|
||||
assert!(form.height >= 100. && form.y + form.height <= size.height);
|
||||
assert!(actions.y >= canvas.y + canvas.height);
|
||||
assert!(actions.y + actions.height <= form.y);
|
||||
scroll(
|
||||
&mut view,
|
||||
&mut tree,
|
||||
&node,
|
||||
"scenario-form",
|
||||
Vector::new(0., 600.),
|
||||
);
|
||||
let (_, after) = geometry(&mut view, &mut tree, size);
|
||||
assert_eq!(after.actions, Some(actions));
|
||||
assert_eq!(after.scrolls["scenario-form"].1.y, 600.);
|
||||
assert_eq!(after.scrolls["scenario-canvas"].1, Vector::ZERO);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scenario_workspace_canvas_and_form_scroll_survive_disclosures_focus_width_and_resize() {
|
||||
let context = super::super::direct_tests::context(Id::new_v4());
|
||||
let mut state = State::default();
|
||||
let mut view = fixture_view(&state, context, false, 68.);
|
||||
let mut tree = widget::Tree::new(view.as_widget());
|
||||
let (node, _) = geometry(&mut view, &mut tree, Size::new(500., 570.));
|
||||
let offset = Vector::new(90., 180.);
|
||||
scroll(&mut view, &mut tree, &node, "scenario-canvas", offset);
|
||||
let mut prior_form = false;
|
||||
for (pending, shelf, header, size) in [
|
||||
(false, Some(Shelf::Tools), 100., Size::new(500., 570.)),
|
||||
(true, Some(Shelf::Selection), 68., Size::new(1050., 570.)),
|
||||
(true, None, 68., Size::new(1394., 802.)),
|
||||
(true, Some(Shelf::Tools), 100., Size::new(500., 570.)),
|
||||
(false, None, 100., Size::new(500., 570.)),
|
||||
] {
|
||||
state.presentation.shelf = shelf;
|
||||
view = fixture_view(&state, context, pending, header);
|
||||
tree.diff(view.as_widget());
|
||||
let (node, actual) = geometry(&mut view, &mut tree, size);
|
||||
assert_eq!(actual.scrolls["scenario-canvas"].1, offset);
|
||||
if pending {
|
||||
if prior_form {
|
||||
assert_eq!(actual.scrolls["scenario-form"].1.y, 600.);
|
||||
}
|
||||
scroll(
|
||||
&mut view,
|
||||
&mut tree,
|
||||
&node,
|
||||
"scenario-form",
|
||||
Vector::new(0., 600.),
|
||||
);
|
||||
let (_, after) = geometry(&mut view, &mut tree, size);
|
||||
assert_eq!(after.actions, actual.actions);
|
||||
assert_eq!(after.scrolls["scenario-canvas"].1, offset);
|
||||
}
|
||||
prior_form = pending;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scenario_workspace_caller_restores_actual_mounted_canvas_and_shelf_with_camera() {
|
||||
use super::super::direct_tests::{navigation_event, navigation_fixture};
|
||||
let (p, [a, b, _], [reference, _, _]) = navigation_fixture();
|
||||
let mut app = App::with_project(p);
|
||||
let _ = app.update(Message::Select(a));
|
||||
let _ = app.update(Message::Page(crate::app::Page::Behavior));
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::Shelf(ctx, Shelf::Selection));
|
||||
navigation_event(&mut app, CanvasEvent::Focus);
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, Some(0.75)));
|
||||
let source = app.document.snapshot();
|
||||
let size = Size::new(1050., 570.);
|
||||
let mut view = fixture_view(
|
||||
&app.behavior.scenario,
|
||||
app.scenario_context().unwrap(),
|
||||
false,
|
||||
68.,
|
||||
);
|
||||
let mut tree = widget::Tree::new(view.as_widget());
|
||||
redraw(&mut view, &mut tree, size, &mut app);
|
||||
let (node, _) = geometry(&mut view, &mut tree, size);
|
||||
let canvas_offset = Vector::new(90., 180.);
|
||||
scroll(
|
||||
&mut view,
|
||||
&mut tree,
|
||||
&node,
|
||||
"scenario-canvas",
|
||||
canvas_offset,
|
||||
);
|
||||
scroll(
|
||||
&mut view,
|
||||
&mut tree,
|
||||
&node,
|
||||
"scenario-details",
|
||||
Vector::new(0., 80.),
|
||||
);
|
||||
redraw(&mut view, &mut tree, size, &mut app);
|
||||
assert_eq!(
|
||||
app.behavior.scenario.scroll_offset(),
|
||||
canvas_offset,
|
||||
"shelf is not a canvas ancestor"
|
||||
);
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::OpenUse(ctx, reference));
|
||||
assert_eq!(app.selected, b);
|
||||
view = fixture_view(
|
||||
&app.behavior.scenario,
|
||||
app.scenario_context().unwrap(),
|
||||
false,
|
||||
68.,
|
||||
);
|
||||
tree.diff(view.as_widget());
|
||||
redraw(&mut view, &mut tree, size, &mut app);
|
||||
let (_, opened) = geometry(&mut view, &mut tree, size);
|
||||
assert_eq!(opened.scrolls["scenario-canvas"].1, Vector::ZERO);
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, Some(2.)));
|
||||
navigation_event(&mut app, CanvasEvent::Focus);
|
||||
navigation_event(&mut app, CanvasEvent::Back);
|
||||
assert_eq!(app.selected, reference);
|
||||
assert!(app.behavior.scenario.focused());
|
||||
assert_eq!(
|
||||
app.behavior.scenario.presentation.shelf,
|
||||
Some(Shelf::Selection)
|
||||
);
|
||||
assert_eq!(
|
||||
app.behavior
|
||||
.scenario
|
||||
.presentation
|
||||
.scale(Size::new(800., 1200.)),
|
||||
0.75
|
||||
);
|
||||
view = fixture_view(
|
||||
&app.behavior.scenario,
|
||||
app.scenario_context().unwrap(),
|
||||
false,
|
||||
68.,
|
||||
);
|
||||
tree.diff(view.as_widget());
|
||||
redraw(&mut view, &mut tree, size, &mut app);
|
||||
let (node, returned) = geometry(&mut view, &mut tree, size);
|
||||
assert_eq!(returned.scrolls["scenario-canvas"].1, canvas_offset);
|
||||
assert_eq!(returned.scrolls["scenario-details"].1, Vector::new(0., 80.));
|
||||
for area in [ScrollArea::Canvas, ScrollArea::Page] {
|
||||
assert!(
|
||||
app.behavior
|
||||
.scenario
|
||||
.scroll_restore(app.scenario_context().unwrap(), area)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
scroll(
|
||||
&mut view,
|
||||
&mut tree,
|
||||
&node,
|
||||
"scenario-canvas",
|
||||
Vector::new(90., 220.),
|
||||
);
|
||||
redraw(&mut view, &mut tree, size, &mut app);
|
||||
assert_eq!(
|
||||
geometry(&mut view, &mut tree, size).1.scrolls["scenario-canvas"]
|
||||
.1
|
||||
.y,
|
||||
220.,
|
||||
"receipt cannot snap a later gesture back"
|
||||
);
|
||||
assert!(std::sync::Arc::ptr_eq(&source, &app.document.snapshot()));
|
||||
assert!(!app.document.can_undo());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scenario_workspace_fit_uses_measured_inner_viewport_and_rejects_nonfinite_inputs() {
|
||||
let graph = Size::new(900., 1400.);
|
||||
let mut presentation = Presentation::default();
|
||||
assert_eq!(presentation.scale(graph), 1.);
|
||||
presentation.zoom(None);
|
||||
assert_eq!(
|
||||
presentation.scale(graph),
|
||||
1.,
|
||||
"no invented window-height fallback"
|
||||
);
|
||||
assert!(presentation.viewport(Size::new(484., 204.)));
|
||||
assert!(!presentation.viewport(Size::new(484., 204.)));
|
||||
let scale = presentation.scale(graph);
|
||||
assert!(graph.width * scale <= 468. && graph.height * scale <= 188.);
|
||||
assert!(!presentation.fit_limited(graph));
|
||||
for bad in [f32::NAN, f32::INFINITY, 0., -1.] {
|
||||
assert!(!presentation.zoom(Some(bad)));
|
||||
assert!(!presentation.viewport(Size::new(bad, 100.)));
|
||||
assert!(!presentation.viewport(Size::new(100., bad)));
|
||||
assert_eq!(presentation.scale(graph), scale);
|
||||
}
|
||||
assert!(presentation.fit_limited(Size::new(900., 10000.)));
|
||||
assert!(presentation.zoom(Some(1.25)));
|
||||
presentation.viewport(Size::new(200., 100.));
|
||||
assert_eq!(
|
||||
presentation.scale(graph),
|
||||
1.25,
|
||||
"manual zoom survives form/viewport resize"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scenario_workspace_same_size_owner_and_document_changes_remeasure_fit_viewport() {
|
||||
use super::super::direct_tests::{navigation_event, navigation_fixture};
|
||||
let (project, [a, b, _], [reference, _, _]) = navigation_fixture();
|
||||
let mut app = App::with_project(project.clone());
|
||||
let _ = app.update(Message::Select(a));
|
||||
let _ = app.update(Message::Page(crate::app::Page::Behavior));
|
||||
let size = Size::new(500., 570.);
|
||||
let graph = Size::new(1800., 2400.);
|
||||
let mut view = fixture_view(
|
||||
&app.behavior.scenario,
|
||||
app.scenario_context().unwrap(),
|
||||
false,
|
||||
68.,
|
||||
);
|
||||
let mut tree = widget::Tree::new(view.as_widget());
|
||||
redraw(&mut view, &mut tree, size, &mut app);
|
||||
let first_viewport = app.behavior.scenario.presentation.viewport.unwrap();
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, None));
|
||||
assert!(app.behavior.scenario.presentation.scale(graph) < 1.);
|
||||
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::OpenUse(ctx, reference));
|
||||
assert_eq!(app.scenario_context().unwrap().scenario, b);
|
||||
assert!(app.behavior.scenario.presentation.viewport.is_none());
|
||||
view = fixture_view(
|
||||
&app.behavior.scenario,
|
||||
app.scenario_context().unwrap(),
|
||||
false,
|
||||
68.,
|
||||
);
|
||||
tree.diff(view.as_widget());
|
||||
redraw(&mut view, &mut tree, size, &mut app);
|
||||
assert_eq!(
|
||||
app.behavior.scenario.presentation.viewport,
|
||||
Some(first_viewport)
|
||||
);
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, None));
|
||||
let fitted = app.behavior.scenario.presentation.scale(graph);
|
||||
assert!(graph.width * fitted <= first_viewport.width - 16.);
|
||||
assert!(graph.height * fitted <= first_viewport.height - 16.);
|
||||
|
||||
let prior_document = app.scenario_context().unwrap().document;
|
||||
app = App::with_project(project);
|
||||
let _ = app.update(Message::Select(b));
|
||||
let _ = app.update(Message::Page(crate::app::Page::Behavior));
|
||||
let context = app.scenario_context().unwrap();
|
||||
assert_ne!(context.document, prior_document);
|
||||
assert_eq!(
|
||||
context.scenario, b,
|
||||
"semantic UUID is retained across documents"
|
||||
);
|
||||
assert!(app.behavior.scenario.presentation.viewport.is_none());
|
||||
let accepted = app.document.snapshot();
|
||||
view = fixture_view(&app.behavior.scenario, context, false, 68.);
|
||||
tree.diff(view.as_widget());
|
||||
redraw(&mut view, &mut tree, size, &mut app);
|
||||
assert_eq!(
|
||||
app.behavior.scenario.presentation.viewport,
|
||||
Some(first_viewport)
|
||||
);
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, None));
|
||||
assert_eq!(app.behavior.scenario.presentation.scale(graph), fitted);
|
||||
assert!(std::sync::Arc::ptr_eq(&accepted, &app.document.snapshot()));
|
||||
assert!(!app.document.can_undo());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scenario_workspace_open_and_back_restore_owner_and_mounted_scroll_after_preselection() {
|
||||
use super::super::direct_tests::{navigation_event, navigation_fixture};
|
||||
let (p, [a, b, _], [reference, _, _]) = navigation_fixture();
|
||||
let mut app = App::with_project(p);
|
||||
let _ = app.update(Message::Select(a));
|
||||
let _ = app.update(Message::Page(crate::app::Page::Behavior));
|
||||
navigation_event(&mut app, |ctx| {
|
||||
CanvasEvent::Choose(ctx, CanvasTool::Message(MessageStyle::Timer))
|
||||
});
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::Shelf(ctx, Shelf::Selection));
|
||||
navigation_event(&mut app, CanvasEvent::Focus);
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, Some(0.75)));
|
||||
let size = Size::new(1050., 570.);
|
||||
let mut view = fixture_view(
|
||||
&app.behavior.scenario,
|
||||
app.scenario_context().unwrap(),
|
||||
false,
|
||||
68.,
|
||||
);
|
||||
let mut tree = widget::Tree::new(view.as_widget());
|
||||
redraw(&mut view, &mut tree, size, &mut app);
|
||||
let (node, _) = geometry(&mut view, &mut tree, size);
|
||||
let offset = Vector::new(90., 180.);
|
||||
scroll(&mut view, &mut tree, &node, "scenario-canvas", offset);
|
||||
scroll(
|
||||
&mut view,
|
||||
&mut tree,
|
||||
&node,
|
||||
"scenario-details",
|
||||
Vector::new(0., 80.),
|
||||
);
|
||||
redraw(&mut view, &mut tree, size, &mut app);
|
||||
|
||||
let _ = app.update(Message::Command(Command::Rename(
|
||||
b,
|
||||
"Renamed definition".into(),
|
||||
)));
|
||||
assert_eq!(app.selected, b);
|
||||
assert_eq!(app.scenario_context().unwrap().scenario, a);
|
||||
let before_open = app.document.snapshot();
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::OpenUse(ctx, reference));
|
||||
assert_eq!(app.selected, b);
|
||||
assert_eq!(app.scenario_context().unwrap().scenario, b);
|
||||
assert!(std::sync::Arc::ptr_eq(
|
||||
&before_open,
|
||||
&app.document.snapshot()
|
||||
));
|
||||
view = fixture_view(
|
||||
&app.behavior.scenario,
|
||||
app.scenario_context().unwrap(),
|
||||
false,
|
||||
68.,
|
||||
);
|
||||
tree.diff(view.as_widget());
|
||||
redraw(&mut view, &mut tree, size, &mut app);
|
||||
let (_, opened) = geometry(&mut view, &mut tree, size);
|
||||
assert_eq!(opened.scrolls["scenario-canvas"].1, Vector::ZERO);
|
||||
|
||||
let _ = app.update(Message::Command(Command::Rename(
|
||||
reference,
|
||||
"Renamed caller use".into(),
|
||||
)));
|
||||
assert_eq!(app.selected, reference);
|
||||
assert_eq!(app.scenario_context().unwrap().scenario, b);
|
||||
let before_back = app.document.snapshot();
|
||||
let revision = app.document.revision();
|
||||
navigation_event(&mut app, CanvasEvent::Back);
|
||||
assert_eq!(app.selected, reference);
|
||||
assert_eq!(app.scenario_context().unwrap().scenario, a);
|
||||
assert_eq!(
|
||||
app.behavior.scenario.canvas_tool(a),
|
||||
CanvasTool::Message(MessageStyle::Timer)
|
||||
);
|
||||
assert!(app.behavior.scenario.focused());
|
||||
assert_eq!(
|
||||
app.behavior.scenario.presentation.shelf,
|
||||
Some(Shelf::Selection)
|
||||
);
|
||||
assert_eq!(app.behavior.scenario.presentation.zoom, Some(0.75));
|
||||
view = fixture_view(
|
||||
&app.behavior.scenario,
|
||||
app.scenario_context().unwrap(),
|
||||
false,
|
||||
68.,
|
||||
);
|
||||
tree.diff(view.as_widget());
|
||||
redraw(&mut view, &mut tree, size, &mut app);
|
||||
let (_, restored) = geometry(&mut view, &mut tree, size);
|
||||
assert_eq!(restored.scrolls["scenario-canvas"].1, offset);
|
||||
assert_eq!(restored.scrolls["scenario-details"].1, Vector::new(0., 80.));
|
||||
assert_eq!(app.behavior.scenario.scroll_offset(), offset);
|
||||
assert!(app.behavior.scenario.restore.is_none());
|
||||
assert!(std::sync::Arc::ptr_eq(
|
||||
&before_back,
|
||||
&app.document.snapshot()
|
||||
));
|
||||
assert_eq!(app.document.revision(), revision);
|
||||
assert!(app.document.can_undo());
|
||||
assert!(!app.document.can_redo());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scenario_workspace_viewport_events_are_passive_guarded_and_do_not_apply_raw_forms() {
|
||||
use super::super::direct_tests::{fixture, navigation_event};
|
||||
let f = fixture();
|
||||
let mut app = App::with_project(f.project);
|
||||
let _ = app.update(Message::Select(f.owner));
|
||||
let _ = app.update(Message::Page(crate::app::Page::Behavior));
|
||||
let _ = app.update(event(Event::Start(f.owner, Tool::Message)));
|
||||
let _ = app.update(event(Event::Name(" raw 雨 ".into())));
|
||||
let raw = app.behavior.recovery_snapshot().unwrap();
|
||||
let accepted = app.document.snapshot();
|
||||
let context = app.scenario_context().unwrap();
|
||||
app.busy = true;
|
||||
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Viewport(
|
||||
context,
|
||||
Size::new(500., 200.),
|
||||
)));
|
||||
assert_ne!(app.scenario_context(), Some(context));
|
||||
let epoch = app.canvas_epoch;
|
||||
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Viewport(
|
||||
context,
|
||||
Size::new(500., 200.),
|
||||
)));
|
||||
assert_eq!(app.canvas_epoch, epoch);
|
||||
for mismatch in 0..3 {
|
||||
let mut stale = context;
|
||||
match mismatch {
|
||||
0 => stale.document = Id::new_v4(),
|
||||
1 => stale.scenario = Id::new_v4(),
|
||||
_ => stale.revision += 1,
|
||||
}
|
||||
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Viewport(
|
||||
stale,
|
||||
Size::new(900., 400.),
|
||||
)));
|
||||
assert_eq!(app.canvas_epoch, epoch);
|
||||
}
|
||||
app.busy = false;
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::Shelf(ctx, Shelf::Tools));
|
||||
navigation_event(&mut app, CanvasEvent::Focus);
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, None));
|
||||
assert_eq!(app.behavior.recovery_snapshot().unwrap(), raw);
|
||||
assert!(std::sync::Arc::ptr_eq(&accepted, &app.document.snapshot()));
|
||||
assert!(!app.document.can_undo());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scenario_workspace_choosing_tools_preserves_valid_and_invalid_pending_editors() {
|
||||
use super::super::direct_tests::{fixture, navigation_event};
|
||||
for editor in 0..3 {
|
||||
for valid in [false, true] {
|
||||
let f = fixture();
|
||||
let mut app = App::with_project(f.project);
|
||||
let _ = app.update(Message::Select(f.owner));
|
||||
let _ = app.update(Message::Page(crate::app::Page::Behavior));
|
||||
match editor {
|
||||
0 => {
|
||||
let _ = app.update(Message::Field(
|
||||
if valid { "/name" } else { "/id" }.into(),
|
||||
if valid {
|
||||
" Retained property 雨 "
|
||||
} else {
|
||||
"not-a-uuid"
|
||||
}
|
||||
.into(),
|
||||
));
|
||||
assert!(app.draft_dirty());
|
||||
assert_eq!(app.draft.as_ref().unwrap().command().is_ok(), valid);
|
||||
}
|
||||
1 => {
|
||||
let raw = if valid {
|
||||
format!("// Retained source 雨\n{}", app.source_editor.text())
|
||||
} else {
|
||||
"? invalid source 雨".into()
|
||||
};
|
||||
let _ = app.update(Message::SourceEdit(
|
||||
iced::widget::text_editor::Action::SelectAll,
|
||||
));
|
||||
let _ = app.update(Message::SourceEdit(
|
||||
iced::widget::text_editor::Action::Edit(
|
||||
iced::widget::text_editor::Edit::Paste(std::sync::Arc::new(raw)),
|
||||
),
|
||||
));
|
||||
assert!(app.source_dirty());
|
||||
assert_eq!(
|
||||
crate::source_workspace::compile(&app.source_workspace.bundle()).is_ok(),
|
||||
valid
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
let _ = app.update(event(Event::Start(
|
||||
f.owner,
|
||||
if valid { Tool::Fragment } else { Tool::Action },
|
||||
)));
|
||||
let _ = app.update(event(Event::Name(" Retained behavior 雨 ".into())));
|
||||
let _ = app.update(event(Event::Expression("ready".into())));
|
||||
let _ = app.update(event(Event::SecondGuard("otherwise".into())));
|
||||
assert!(app.behavior.has_pending());
|
||||
assert_eq!(
|
||||
app.behavior
|
||||
.scenario
|
||||
.draft
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.command(app.project())
|
||||
.is_ok(),
|
||||
valid
|
||||
);
|
||||
}
|
||||
}
|
||||
let source = serde_json::to_value(app.source_workspace.recovery_snapshot()).unwrap();
|
||||
let properties = app
|
||||
.draft
|
||||
.as_ref()
|
||||
.map(|draft| serde_json::to_value(draft.recovery_snapshot()).unwrap());
|
||||
let behavior = app.behavior.recovery_snapshot().unwrap();
|
||||
let text = app.source_editor.text();
|
||||
let accepted = app.document.snapshot();
|
||||
let selected = app.selected;
|
||||
let context = app.scenario_context();
|
||||
let tool = app.behavior.scenario.canvas_tool(f.owner);
|
||||
navigation_event(&mut app, |ctx| {
|
||||
CanvasEvent::Choose(ctx, CanvasTool::Message(MessageStyle::Timer))
|
||||
});
|
||||
assert!(
|
||||
app.status.contains("Apply or cancel"),
|
||||
"editor={editor} valid={valid}: {}",
|
||||
app.status
|
||||
);
|
||||
assert_eq!(app.source_editor.text(), text);
|
||||
assert_eq!(
|
||||
serde_json::to_value(app.source_workspace.recovery_snapshot()).unwrap(),
|
||||
source
|
||||
);
|
||||
assert_eq!(
|
||||
app.draft
|
||||
.as_ref()
|
||||
.map(|draft| serde_json::to_value(draft.recovery_snapshot()).unwrap()),
|
||||
properties
|
||||
);
|
||||
assert_eq!(app.behavior.recovery_snapshot().unwrap(), behavior);
|
||||
assert_eq!(app.selected, selected);
|
||||
assert_eq!(app.scenario_context(), context);
|
||||
assert_eq!(app.behavior.scenario.canvas_tool(f.owner), tool);
|
||||
assert!(std::sync::Arc::ptr_eq(&accepted, &app.document.snapshot()));
|
||||
assert!(!app.document.can_undo());
|
||||
assert!(!app.document.can_redo());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scenario_workspace_whole_app_canvas_tree_survives_pending_shelves_and_focus() {
|
||||
use iced::advanced::widget::tree;
|
||||
use std::rc::Rc;
|
||||
fn path_to(tree: &widget::Tree, tag: tree::Tag) -> Option<Vec<usize>> {
|
||||
if tree.tag == tag {
|
||||
return Some(vec![]);
|
||||
}
|
||||
tree.children.iter().enumerate().find_map(|(i, child)| {
|
||||
let mut path = path_to(child, tag)?;
|
||||
path.insert(0, i);
|
||||
Some(path)
|
||||
})
|
||||
}
|
||||
fn at<'a>(mut tree: &'a mut widget::Tree, path: &[usize]) -> &'a mut widget::Tree {
|
||||
for &index in path {
|
||||
tree = &mut tree.children[index];
|
||||
}
|
||||
tree
|
||||
}
|
||||
let f = super::super::direct_tests::fixture();
|
||||
let mut app = App::with_project(f.project);
|
||||
let _ = app.update(Message::Select(f.call));
|
||||
let _ = app.update(Message::Page(crate::app::Page::Behavior));
|
||||
let canvas: Element<'_, Message> =
|
||||
canvas::Canvas::new(Scene::sequence(app.project(), f.owner, f.call)).into();
|
||||
let tag = widget::Tree::new(canvas.as_widget()).tag;
|
||||
drop(canvas);
|
||||
let mut tree = widget::Tree::new(crate::views::view(&app).as_widget());
|
||||
let mut path = path_to(&tree, tag).unwrap();
|
||||
path.pop();
|
||||
let marker = Rc::new(());
|
||||
at(&mut tree, &path).state = tree::State::new(Rc::clone(&marker));
|
||||
let _ = app.update(event(Event::Start(f.owner, Tool::Message)));
|
||||
let _ = app.update(event(Event::Name(" untouched 雨 ".into())));
|
||||
let raw = app.behavior.recovery_snapshot().unwrap();
|
||||
let accepted = app.document.snapshot();
|
||||
for shelf in [Shelf::Tools, Shelf::Selection, Shelf::Selection] {
|
||||
super::super::direct_tests::navigation_event(&mut app, |ctx| {
|
||||
CanvasEvent::Shelf(ctx, shelf)
|
||||
});
|
||||
super::super::direct_tests::navigation_event(&mut app, CanvasEvent::Focus);
|
||||
tree.diff(crate::views::view(&app).as_widget());
|
||||
let mut current = path_to(&tree, tag).unwrap();
|
||||
current.pop();
|
||||
assert_eq!(current, path);
|
||||
assert_eq!(Rc::strong_count(&marker), 2);
|
||||
assert!(Rc::ptr_eq(
|
||||
at(&mut tree, &path).state.downcast_ref::<Rc<()>>(),
|
||||
&marker
|
||||
));
|
||||
assert_eq!(app.behavior.recovery_snapshot().unwrap(), raw);
|
||||
assert_eq!(app.selected, f.call);
|
||||
assert!(std::sync::Arc::ptr_eq(&accepted, &app.document.snapshot()));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scenario_workspace_scaled_and_scrolled_scene_keeps_clicked_slots_and_message_endpoints() {
|
||||
let f = super::super::direct_tests::fixture();
|
||||
for scale in [0.25, 0.5, 1., 2.] {
|
||||
for drag in [false, true] {
|
||||
let mut scene = Scene::sequence(&f.project, f.owner, f.call);
|
||||
let context = super::super::direct_tests::context(f.owner);
|
||||
scene.scale = scale;
|
||||
scene.scroll_offset = Vector::new(35., 40.);
|
||||
scene.interaction = Some((context, CanvasTool::Message(MessageStyle::CallReply), true));
|
||||
let slot = scene.slots.last().unwrap();
|
||||
let expected = slot.slot;
|
||||
let bounds = Rectangle::new(
|
||||
Point::new(20., 80.),
|
||||
Size::new(scene.size.width * scale, scene.size.height * scale),
|
||||
);
|
||||
let screen = |world: Point| {
|
||||
Point::new(bounds.x + world.x * scale, bounds.y + world.y * scale)
|
||||
- scene.scroll_offset
|
||||
};
|
||||
let a = screen(Point::new(scene.lanes[&f.sender], slot.y));
|
||||
let b = screen(Point::new(scene.lanes[&f.receiver], slot.y));
|
||||
let mut input = super::super::super::SequenceInput::default();
|
||||
let mut messages = vec![];
|
||||
let mut events = vec![
|
||||
(mouse::Event::CursorMoved { position: a }, a),
|
||||
(mouse::Event::ButtonPressed(mouse::Button::Left), a),
|
||||
];
|
||||
if !drag {
|
||||
events.push((mouse::Event::ButtonReleased(mouse::Button::Left), a));
|
||||
}
|
||||
events.push((mouse::Event::CursorMoved { position: b }, b));
|
||||
if !drag {
|
||||
events.push((mouse::Event::ButtonPressed(mouse::Button::Left), b));
|
||||
}
|
||||
events.push((mouse::Event::ButtonReleased(mouse::Button::Left), b));
|
||||
for (event, cursor) in events {
|
||||
if let Some(action) = canvas::Program::update(
|
||||
&scene,
|
||||
&mut input,
|
||||
&canvas::Event::Mouse(event),
|
||||
bounds,
|
||||
mouse::Cursor::Available(cursor + scene.scroll_offset),
|
||||
) && let Some(message) = action.into_inner().0
|
||||
{
|
||||
messages.push(message);
|
||||
}
|
||||
}
|
||||
assert!(
|
||||
matches!(messages.as_slice(), [Message::ScenarioCanvas(CanvasEvent::Begin { sender, receiver, slot, .. })]
|
||||
if *sender == f.sender && *receiver == f.receiver && *slot == expected),
|
||||
"scale={scale} drag={drag}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scenario_workspace_scaled_nested_occurrences_and_reorder_keep_exact_sequence_identity() {
|
||||
let (f, branch, anchor, _, _, _) = super::super::direct_tests::placement_fixture();
|
||||
for scale in [0.25, 0.5, 1., 2.] {
|
||||
let mut scene = Scene::sequence(&f.project, f.owner, f.call);
|
||||
let context = super::super::direct_tests::context(f.owner);
|
||||
scene.scale = scale;
|
||||
scene.scroll_offset = Vector::new(35., 40.);
|
||||
let bounds = Rectangle::new(
|
||||
Point::new(20., 80.),
|
||||
Size::new(scene.size.width * scale, scene.size.height * scale),
|
||||
);
|
||||
let screen = |world: Point| {
|
||||
Point::new(bounds.x + world.x * scale, bounds.y + world.y * scale)
|
||||
- Vector::new(35., 40.)
|
||||
};
|
||||
let send =
|
||||
|scene: &Scene, input: &mut super::super::super::SequenceInput, event, cursor| {
|
||||
canvas::Program::update(
|
||||
scene,
|
||||
input,
|
||||
&canvas::Event::Mouse(event),
|
||||
bounds,
|
||||
mouse::Cursor::Available(cursor + scene.scroll_offset),
|
||||
)
|
||||
.and_then(|action| action.into_inner().0)
|
||||
};
|
||||
for tool in [Tool::Action, Tool::Observation, Tool::Reuse, Tool::Fragment] {
|
||||
scene.interaction = Some((context, CanvasTool::Add(tool), true));
|
||||
for expected in [
|
||||
Slot {
|
||||
parent: f.owner,
|
||||
before: Some(f.reply),
|
||||
},
|
||||
Slot {
|
||||
parent: branch,
|
||||
before: Some(anchor),
|
||||
},
|
||||
] {
|
||||
let slot = scene
|
||||
.slots
|
||||
.iter()
|
||||
.find(|slot| slot.slot == expected)
|
||||
.unwrap();
|
||||
let point = screen(Point::new(scene.lanes[&f.sender], slot.y));
|
||||
let mut input = super::super::super::SequenceInput::default();
|
||||
send(
|
||||
&scene,
|
||||
&mut input,
|
||||
mouse::Event::CursorMoved { position: point },
|
||||
point,
|
||||
);
|
||||
assert!(
|
||||
send(
|
||||
&scene,
|
||||
&mut input,
|
||||
mouse::Event::ButtonPressed(mouse::Button::Left),
|
||||
point
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
let message = send(
|
||||
&scene,
|
||||
&mut input,
|
||||
mouse::Event::ButtonReleased(mouse::Button::Left),
|
||||
point,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
matches!(message, Message::ScenarioCanvas(CanvasEvent::BeginOccurrence { tool: captured, slot, lifeline, .. })
|
||||
if captured == tool && slot == expected && lifeline == tool.uses_lifeline().then_some(f.sender)),
|
||||
"scale={scale} tool={tool:?} slot={expected:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
scene.interaction = Some((context, CanvasTool::Select, true));
|
||||
let mut input = super::super::super::SequenceInput::default();
|
||||
let expected = scene
|
||||
.slots
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|slot| slot.slot.parent == f.owner)
|
||||
.unwrap();
|
||||
let start = screen(scene.reorder_handle().unwrap().center());
|
||||
let end = screen(Point::new(17., expected.y));
|
||||
send(
|
||||
&scene,
|
||||
&mut input,
|
||||
mouse::Event::CursorMoved { position: start },
|
||||
start,
|
||||
);
|
||||
send(
|
||||
&scene,
|
||||
&mut input,
|
||||
mouse::Event::ButtonPressed(mouse::Button::Left),
|
||||
start,
|
||||
);
|
||||
send(
|
||||
&scene,
|
||||
&mut input,
|
||||
mouse::Event::CursorMoved { position: end },
|
||||
end,
|
||||
);
|
||||
assert!(
|
||||
matches!(send(&scene, &mut input, mouse::Event::ButtonReleased(mouse::Button::Left), end),
|
||||
Some(Message::ScenarioCanvas(CanvasEvent::Reorder { occurrence, slot, .. }))
|
||||
if occurrence == f.call && slot == expected.slot),
|
||||
"scale={scale}"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
//! Read-only lifecycle geometry. Operand screen order is never execution order.
|
||||
use oa_core::{FragmentOperator, Id, Kind, MessageKind, OccurrenceBody, Project, SharedBody};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
pub(super) mod render;
|
||||
|
||||
/// Maps already recorded by the sequence renderer. Rows carry their owning sequence.
|
||||
pub(crate) struct Geometry<'a> {
|
||||
pub rows: &'a BTreeMap<Id, (Id, f32)>,
|
||||
pub scopes: &'a BTreeMap<Id, (f32, f32)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub(crate) struct Span {
|
||||
pub start: f32,
|
||||
pub end: f32,
|
||||
}
|
||||
impl Span {
|
||||
fn valid(self) -> bool {
|
||||
self.start.is_finite() && self.end.is_finite() && self.start <= self.end
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum Life {
|
||||
Alive,
|
||||
Absent,
|
||||
Unresolved,
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub(crate) struct Interval {
|
||||
pub scope: Id,
|
||||
pub span: Span,
|
||||
pub life: Life,
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum MarkerKind {
|
||||
Birth,
|
||||
Death,
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub(crate) struct Marker {
|
||||
pub occurrence: Id,
|
||||
pub scope: Id,
|
||||
pub y: f32,
|
||||
pub kind: MarkerKind,
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum UncertaintyReason {
|
||||
Conditional,
|
||||
Repeated,
|
||||
Parallel,
|
||||
MissingGeometry,
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) struct Uncertainty {
|
||||
pub scope: Id,
|
||||
pub reason: UncertaintyReason,
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct LaneLifetime {
|
||||
pub lifeline: Id,
|
||||
pub intervals: Vec<Interval>,
|
||||
pub markers: Vec<Marker>,
|
||||
pub uncertainties: Vec<Uncertainty>,
|
||||
}
|
||||
|
||||
/// Stable lane-ID order; source/model/history and caller-owned geometry are untouched.
|
||||
/// An opaque ScenarioUse is a local use, not a caller birth/death contract.
|
||||
pub(crate) fn project_lifetimes(
|
||||
project: &Project,
|
||||
scenario: Id,
|
||||
geometry: &Geometry<'_>,
|
||||
) -> Vec<LaneLifetime> {
|
||||
if project
|
||||
.elements
|
||||
.get(&scenario)
|
||||
.is_none_or(|e| e.kind() != Kind::Scenario)
|
||||
{
|
||||
return vec![];
|
||||
}
|
||||
project
|
||||
.elements
|
||||
.values()
|
||||
.filter(|e| e.kind() == Kind::Lifeline && e.parent == Some(scenario))
|
||||
.map(|e| {
|
||||
let mut projection = Projection {
|
||||
project,
|
||||
geometry,
|
||||
lane: e.id,
|
||||
visiting: BTreeSet::new(),
|
||||
result: LaneLifetime {
|
||||
lifeline: e.id,
|
||||
intervals: vec![],
|
||||
markers: vec![],
|
||||
uncertainties: vec![],
|
||||
},
|
||||
};
|
||||
let initial = if projection.contains(scenario, Some(MarkerKind::Birth)) {
|
||||
Life::Unresolved
|
||||
} else {
|
||||
Life::Alive
|
||||
};
|
||||
projection.sequence(scenario, initial);
|
||||
projection.result
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
struct Projection<'a, 'g> {
|
||||
project: &'a Project,
|
||||
geometry: &'a Geometry<'g>,
|
||||
lane: Id,
|
||||
visiting: BTreeSet<Id>,
|
||||
result: LaneLifetime,
|
||||
}
|
||||
impl Projection<'_, '_> {
|
||||
fn body(&self, id: Id) -> Option<&OccurrenceBody> {
|
||||
match self.project.elements.get(&id)?.shared()? {
|
||||
SharedBody::Occurrence { body, .. } => Some(body),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn event(&self, id: Id) -> Option<MarkerKind> {
|
||||
match self.body(id)? {
|
||||
OccurrenceBody::Exchange {
|
||||
receiver: Some(line),
|
||||
message_kind,
|
||||
..
|
||||
} if *line == self.lane => match message_kind {
|
||||
MessageKind::Create => Some(MarkerKind::Birth),
|
||||
MessageKind::Delete => Some(MarkerKind::Death),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn contains(&self, scope: Id, kind: Option<MarkerKind>) -> bool {
|
||||
// Local structural walk only: no guards, iteration simulation or reuse expansion.
|
||||
let mut pending = vec![scope];
|
||||
let mut seen = BTreeSet::new();
|
||||
while let Some(id) = pending.pop() {
|
||||
if !seen.insert(id) {
|
||||
continue;
|
||||
}
|
||||
if self
|
||||
.event(id)
|
||||
.is_some_and(|event| kind.is_none_or(|k| event == k))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if let Some(steps) = self.project.elements.get(&id).and_then(|e| e.steps()) {
|
||||
pending.extend(steps);
|
||||
}
|
||||
if let Some(OccurrenceBody::Fragment { branches, .. }) = self.body(id) {
|
||||
pending.extend(branches);
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
fn span(&self, scope: Id) -> Option<Span> {
|
||||
let &(start, end) = self.geometry.scopes.get(&scope)?;
|
||||
let span = Span { start, end };
|
||||
span.valid().then_some(span)
|
||||
}
|
||||
fn uncertain(&mut self, scope: Id, reason: UncertaintyReason) {
|
||||
let value = Uncertainty { scope, reason };
|
||||
if !self.result.uncertainties.contains(&value) {
|
||||
self.result.uncertainties.push(value);
|
||||
}
|
||||
}
|
||||
fn interval(&mut self, scope: Id, start: f32, end: f32, life: Life) {
|
||||
if start < end && start.is_finite() && end.is_finite() {
|
||||
self.result.intervals.push(Interval {
|
||||
scope,
|
||||
span: Span { start, end },
|
||||
life,
|
||||
});
|
||||
}
|
||||
}
|
||||
fn sequence(&mut self, scope: Id, entry: Life) -> Life {
|
||||
let Some(span) = self.span(scope) else {
|
||||
self.uncertain(scope, UncertaintyReason::MissingGeometry);
|
||||
return Life::Unresolved;
|
||||
};
|
||||
if !self.visiting.insert(scope) {
|
||||
self.uncertain(scope, UncertaintyReason::MissingGeometry);
|
||||
return Life::Unresolved;
|
||||
}
|
||||
let steps = self
|
||||
.project
|
||||
.elements
|
||||
.get(&scope)
|
||||
.and_then(|e| e.steps())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
// A creation directly in this sequence fixes its local birth boundary.
|
||||
// A creation only in a nested operand cannot fix its ancestor's entry.
|
||||
let mut life = if steps
|
||||
.iter()
|
||||
.any(|&id| self.event(id) == Some(MarkerKind::Birth))
|
||||
{
|
||||
Life::Absent
|
||||
} else {
|
||||
entry
|
||||
};
|
||||
let mut cursor = span.start;
|
||||
for id in steps {
|
||||
let event = self.event(id);
|
||||
let fragment = match self.body(id) {
|
||||
Some(OccurrenceBody::Fragment { operator, branches }) => {
|
||||
Some((*operator, branches.clone()))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
if event.is_none() && fragment.is_none() {
|
||||
continue;
|
||||
}
|
||||
let y = self
|
||||
.geometry
|
||||
.rows
|
||||
.get(&id)
|
||||
.filter(|(owner, y)| {
|
||||
*owner == scope && y.is_finite() && *y >= cursor && *y <= span.end
|
||||
})
|
||||
.map(|(_, y)| *y);
|
||||
let Some(y) = y else {
|
||||
self.uncertain(id, UncertaintyReason::MissingGeometry);
|
||||
life = Life::Unresolved;
|
||||
continue;
|
||||
};
|
||||
self.interval(scope, cursor, y, life);
|
||||
cursor = y;
|
||||
if let Some(kind) = event {
|
||||
self.result.markers.push(Marker {
|
||||
occurrence: id,
|
||||
scope,
|
||||
y,
|
||||
kind,
|
||||
});
|
||||
life = match kind {
|
||||
MarkerKind::Birth => Life::Alive,
|
||||
MarkerKind::Death => Life::Absent,
|
||||
};
|
||||
}
|
||||
if let Some((operator, branches)) = fragment {
|
||||
let entry = life;
|
||||
let mut missing = false;
|
||||
for branch in branches {
|
||||
let bounds = self
|
||||
.span(branch)
|
||||
.filter(|s| s.start >= cursor && s.end <= span.end);
|
||||
if let Some(bounds) = bounds {
|
||||
// Every sibling inherits the same entry, never its displayed predecessor's exit.
|
||||
self.sequence(branch, entry);
|
||||
cursor = bounds.end;
|
||||
} else {
|
||||
self.uncertain(branch, UncertaintyReason::MissingGeometry);
|
||||
missing = true;
|
||||
}
|
||||
}
|
||||
if self.contains(id, None) {
|
||||
let reason = match operator {
|
||||
FragmentOperator::Alternative | FragmentOperator::Optional => {
|
||||
UncertaintyReason::Conditional
|
||||
}
|
||||
FragmentOperator::Loop => UncertaintyReason::Repeated,
|
||||
FragmentOperator::Parallel => UncertaintyReason::Parallel,
|
||||
};
|
||||
self.uncertain(id, reason);
|
||||
life = Life::Unresolved;
|
||||
} else if missing {
|
||||
life = Life::Unresolved;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.interval(scope, cursor, span.end, life);
|
||||
self.visiting.remove(&scope);
|
||||
life
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "scenario_lifecycle/tests.rs"]
|
||||
mod tests;
|
||||
@@ -0,0 +1,377 @@
|
||||
//! Scene bridge for local lifetime geometry; no authoring or model mutations.
|
||||
use super::{Geometry, LaneLifetime, Life, Marker, MarkerKind, Span, project_lifetimes};
|
||||
use crate::{behavior::Line, style, views::short};
|
||||
use iced::{
|
||||
Color, Point, Rectangle, Size,
|
||||
widget::canvas::{self, Frame, Path, Stroke, Text},
|
||||
};
|
||||
use oa_core::{Id, OccurrenceBody, Project, SharedBody};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
struct EventMarker {
|
||||
event: Marker,
|
||||
lifeline: Id,
|
||||
center: Point,
|
||||
capsule: Rectangle,
|
||||
name: String,
|
||||
sender_x: Option<f32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq)]
|
||||
pub(in crate::behavior) struct Rendering {
|
||||
// Complete rendering positions, including warning-admitted unassigned lanes.
|
||||
// Scene.lanes independently retains the existing authoring eligibility rule.
|
||||
lanes: BTreeMap<Id, f32>,
|
||||
span: Option<Span>,
|
||||
lifetimes: Vec<LaneLifetime>,
|
||||
markers: Vec<EventMarker>,
|
||||
}
|
||||
|
||||
impl Rendering {
|
||||
pub(in crate::behavior) fn new(
|
||||
project: &Project,
|
||||
owner: Id,
|
||||
lanes: &BTreeMap<Id, f32>,
|
||||
rows: &BTreeMap<Id, (Id, f32)>,
|
||||
insertion_scopes: &BTreeMap<Id, (f32, f32)>,
|
||||
height: f32,
|
||||
) -> Self {
|
||||
let span = Span {
|
||||
start: 85.,
|
||||
end: height - 20.,
|
||||
};
|
||||
if !span.valid() {
|
||||
return Self::default();
|
||||
}
|
||||
// Insertion scopes deliberately start later and end earlier. Empty and
|
||||
// Create-only scenarios still have the full visible lifetime extent.
|
||||
let mut scopes = insertion_scopes.clone();
|
||||
for (scope, (start, end)) in &mut scopes {
|
||||
if *scope != owner && *start == *end && start.is_finite() {
|
||||
// An empty operand has 42 px between its insertion axis and
|
||||
// frame end. Match the existing 26 px open-activation allowance.
|
||||
*end = *start + 26.;
|
||||
}
|
||||
}
|
||||
scopes.insert(owner, (span.start, span.end));
|
||||
let lifetimes = project_lifetimes(
|
||||
project,
|
||||
owner,
|
||||
&Geometry {
|
||||
rows,
|
||||
scopes: &scopes,
|
||||
},
|
||||
);
|
||||
let lanes: BTreeMap<_, _> = lanes
|
||||
.iter()
|
||||
.filter(|(_, x)| x.is_finite())
|
||||
.map(|(id, x)| (*id, *x))
|
||||
.collect();
|
||||
let mut markers = Vec::new();
|
||||
for lane in &lifetimes {
|
||||
let Some(&x) = lanes.get(&lane.lifeline) else {
|
||||
continue;
|
||||
};
|
||||
for event in &lane.markers {
|
||||
let sender_x = project
|
||||
.elements
|
||||
.get(&event.occurrence)
|
||||
.and_then(|e| e.shared())
|
||||
.and_then(|body| match body {
|
||||
SharedBody::Occurrence {
|
||||
body:
|
||||
OccurrenceBody::Exchange {
|
||||
sender: Some(sender),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => lanes.get(sender).copied(),
|
||||
_ => None,
|
||||
});
|
||||
markers.push(EventMarker {
|
||||
event: *event,
|
||||
lifeline: lane.lifeline,
|
||||
center: Point::new(x, event.y),
|
||||
capsule: Rectangle::new(
|
||||
Point::new(x - 70., event.y - 14.),
|
||||
Size::new(140., 28.),
|
||||
),
|
||||
name: project.named(lane.lifeline).to_owned(),
|
||||
sender_x,
|
||||
});
|
||||
}
|
||||
}
|
||||
Self {
|
||||
lanes,
|
||||
span: Some(span),
|
||||
lifetimes,
|
||||
markers,
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::behavior) fn align_messages(&self, lines: &mut [Line]) {
|
||||
for marker in &self.markers {
|
||||
let Some(sender) = marker.sender_x else {
|
||||
continue;
|
||||
};
|
||||
let Some(line) = lines
|
||||
.iter_mut()
|
||||
.find(|line| line.id == marker.event.occurrence)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let x = marker.center.x;
|
||||
let y = marker.center.y;
|
||||
let self_message = sender == x;
|
||||
let end_x = match marker.event.kind {
|
||||
MarkerKind::Birth if sender < x => marker.capsule.x,
|
||||
MarkerKind::Birth => marker.capsule.x + marker.capsule.width,
|
||||
MarkerKind::Death => x,
|
||||
};
|
||||
line.points = if self_message {
|
||||
// The receiver is on the recorded occurrence row, including self
|
||||
// lifecycle events. The elbow stays outside the birth capsule.
|
||||
let elbow = x + 112.;
|
||||
line.label_at = Point::new(x + 12., y - 52.);
|
||||
vec![
|
||||
Point::new(x, y - 24.),
|
||||
Point::new(elbow, y - 24.),
|
||||
Point::new(elbow, y),
|
||||
Point::new(end_x, y),
|
||||
]
|
||||
} else {
|
||||
if marker.event.kind == MarkerKind::Birth {
|
||||
// Keep the label above the capsule, including right-to-left
|
||||
// messages whose existing label starts beside the receiver.
|
||||
line.label_at.y = y - 40.;
|
||||
}
|
||||
vec![Point::new(sender, y), Point::new(end_x, y)]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::behavior) fn alive_spans(
|
||||
&self,
|
||||
lifeline: Id,
|
||||
scope: Id,
|
||||
top: f32,
|
||||
bottom: f32,
|
||||
) -> Vec<Span> {
|
||||
if !top.is_finite() || !bottom.is_finite() || bottom <= top {
|
||||
return vec![];
|
||||
}
|
||||
self.lifetimes
|
||||
.iter()
|
||||
.find(|lane| lane.lifeline == lifeline)
|
||||
.into_iter()
|
||||
.flat_map(|lane| &lane.intervals)
|
||||
.filter(|interval| interval.scope == scope && interval.life == Life::Alive)
|
||||
.filter_map(|interval| {
|
||||
let start = top.max(interval.span.start);
|
||||
let end = bottom.min(interval.span.end);
|
||||
(start < end).then_some(Span { start, end })
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(in crate::behavior) fn marker_at(&self, point: Point) -> Option<Id> {
|
||||
self.markers
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|marker| match marker.event.kind {
|
||||
MarkerKind::Birth => marker.capsule.contains(point),
|
||||
MarkerKind::Death => marker_distance(marker, point) <= 1.25,
|
||||
})
|
||||
.map(|marker| marker.event.occurrence)
|
||||
}
|
||||
|
||||
pub(in crate::behavior) fn nearest_marker(&self, point: Point) -> Option<(Id, f32)> {
|
||||
if !point.x.is_finite() || !point.y.is_finite() {
|
||||
return None;
|
||||
}
|
||||
// Exact visible shapes are tested first by Scene. A low-zoom tolerance
|
||||
// must not outrank another marker's actual capsule, a header, or a label.
|
||||
self.markers
|
||||
.iter()
|
||||
.map(|marker| (marker.event.occurrence, marker_distance(marker, point)))
|
||||
.min_by(|a, b| a.1.total_cmp(&b.1).then_with(|| a.0.cmp(&b.0)))
|
||||
}
|
||||
|
||||
pub(in crate::behavior) fn lane_at(&self, point: Point, scale: f32) -> Option<Id> {
|
||||
let span = self.span?;
|
||||
if !scale.is_finite() || scale <= 0. || point.y < span.start || point.y > span.end {
|
||||
return None;
|
||||
}
|
||||
self.lanes
|
||||
.iter()
|
||||
.filter(|(_, x)| (**x - point.x).abs() < 12. / scale)
|
||||
.min_by(|a, b| (a.1 - point.x).abs().total_cmp(&(b.1 - point.x).abs()))
|
||||
.map(|(id, _)| *id)
|
||||
}
|
||||
|
||||
pub(in crate::behavior) fn draw_lanes(&self, frame: &mut Frame, selected: Id) {
|
||||
let Some(span) = self.span else {
|
||||
return;
|
||||
};
|
||||
for (&id, &x) in &self.lanes {
|
||||
frame.stroke(
|
||||
&Path::line(Point::new(x, span.start), Point::new(x, span.end)),
|
||||
Stroke {
|
||||
line_dash: canvas::LineDash {
|
||||
segments: &[2., 5.],
|
||||
offset: 0,
|
||||
},
|
||||
..Stroke::default()
|
||||
.with_width(1.)
|
||||
.with_color(if id == selected {
|
||||
Color::from_rgb8(148, 190, 187)
|
||||
} else {
|
||||
Color::from_rgb8(214, 222, 222)
|
||||
})
|
||||
},
|
||||
);
|
||||
}
|
||||
for lifetime in &self.lifetimes {
|
||||
let Some(&x) = self.lanes.get(&lifetime.lifeline) else {
|
||||
continue;
|
||||
};
|
||||
for interval in &lifetime.intervals {
|
||||
match interval.life {
|
||||
Life::Alive => frame.stroke(
|
||||
&Path::line(
|
||||
Point::new(x, interval.span.start),
|
||||
Point::new(x, interval.span.end),
|
||||
),
|
||||
Stroke::default().with_width(2.).with_color(
|
||||
if lifetime.lifeline == selected {
|
||||
style::TEAL
|
||||
} else {
|
||||
Color::from_rgb8(91, 113, 113)
|
||||
},
|
||||
),
|
||||
),
|
||||
Life::Unresolved if interval.span.end - interval.span.start >= 16. => {
|
||||
frame.fill_text(Text {
|
||||
content: "?".into(),
|
||||
position: Point::new(x + 9., interval.span.start + 2.),
|
||||
size: 11.into(),
|
||||
color: style::MUTED,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !self.lanes.is_empty() {
|
||||
frame.fill_text(Text {
|
||||
content:
|
||||
"Dotted: editing guide · Solid: local lifetime · ?: lifetime depends on path"
|
||||
.into(),
|
||||
position: Point::new(28., span.end + 4.),
|
||||
size: 10.into(),
|
||||
color: style::MUTED,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::behavior) fn draw_markers(&self, frame: &mut Frame, selected: Id, scale: f32) {
|
||||
for marker in &self.markers {
|
||||
let color = if selected == marker.event.occurrence || selected == marker.lifeline {
|
||||
style::TEAL
|
||||
} else {
|
||||
style::INK
|
||||
};
|
||||
match marker.event.kind {
|
||||
MarkerKind::Birth => {
|
||||
let path = Path::rounded_rectangle(
|
||||
marker.capsule.position(),
|
||||
marker.capsule.size(),
|
||||
6.into(),
|
||||
);
|
||||
frame.fill(&path, Color::from_rgb8(243, 248, 248));
|
||||
frame.stroke(
|
||||
&path,
|
||||
Stroke::default().with_color(color).with_width(
|
||||
if selected == marker.event.occurrence {
|
||||
2.5
|
||||
} else {
|
||||
1.5
|
||||
},
|
||||
),
|
||||
);
|
||||
if let Some(clip) = birth_label_clip(marker.capsule, scale, frame.size()) {
|
||||
// WGPU clipped frames start with an identity transform;
|
||||
// neither the parent's scale nor its clip is inherited.
|
||||
frame.with_clip(clip, |label| {
|
||||
label.scale(scale);
|
||||
label.fill_text(Text {
|
||||
content: short(&marker.name, 18),
|
||||
position: marker.capsule.position() + iced::Vector::new(8., 6.),
|
||||
size: 12.into(),
|
||||
color,
|
||||
..Default::default()
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
MarkerKind::Death => {
|
||||
for (a, b) in death_strokes(marker.center) {
|
||||
frame.stroke(
|
||||
&Path::line(a, b),
|
||||
Stroke::default().with_color(color).with_width(2.5),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn birth_label_clip(capsule: Rectangle, scale: f32, viewport: Size) -> Option<Rectangle> {
|
||||
if !scale.is_finite() || scale <= 0. {
|
||||
return None;
|
||||
}
|
||||
Rectangle::new(
|
||||
Point::new((capsule.x + 8.) * scale, (capsule.y + 4.) * scale),
|
||||
Size::new((capsule.width - 16.) * scale, (capsule.height - 8.) * scale),
|
||||
)
|
||||
.intersection(&Rectangle::new(Point::ORIGIN, viewport))
|
||||
}
|
||||
|
||||
fn marker_distance(marker: &EventMarker, point: Point) -> f32 {
|
||||
match marker.event.kind {
|
||||
MarkerKind::Birth => {
|
||||
let dx = (marker.capsule.x - point.x)
|
||||
.max(point.x - marker.capsule.x - marker.capsule.width)
|
||||
.max(0.);
|
||||
let dy = (marker.capsule.y - point.y)
|
||||
.max(point.y - marker.capsule.y - marker.capsule.height)
|
||||
.max(0.);
|
||||
dx.hypot(dy)
|
||||
}
|
||||
MarkerKind::Death => death_strokes(marker.center)
|
||||
.iter()
|
||||
.map(|(a, b)| crate::diagram::distance_to_segment(point, *a, *b))
|
||||
.fold(f32::INFINITY, f32::min),
|
||||
}
|
||||
}
|
||||
|
||||
fn death_strokes(center: Point) -> [(Point, Point); 2] {
|
||||
[
|
||||
(
|
||||
center + iced::Vector::new(-7., -7.),
|
||||
center + iced::Vector::new(7., 7.),
|
||||
),
|
||||
(
|
||||
center + iced::Vector::new(-7., 7.),
|
||||
center + iced::Vector::new(7., -7.),
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "render_tests.rs"]
|
||||
mod tests;
|
||||
@@ -0,0 +1,796 @@
|
||||
use super::*;
|
||||
use crate::{behavior::Scene, document::Document};
|
||||
use iced::Size;
|
||||
use oa_core::*;
|
||||
|
||||
struct Fixture {
|
||||
project: Project,
|
||||
owner: Id,
|
||||
sender: Id,
|
||||
receiver: Id,
|
||||
work: Id,
|
||||
exchange: Id,
|
||||
}
|
||||
impl Fixture {
|
||||
fn new(layer: Layer) -> Self {
|
||||
let mut project = Project::empty();
|
||||
let kind = if layer == Layer::Oa {
|
||||
Kind::Activity
|
||||
} else {
|
||||
Kind::Function
|
||||
};
|
||||
let a = project.add_element(kind, layer, "Request", None).unwrap();
|
||||
let work = project.add_element(kind, layer, "Provide", None).unwrap();
|
||||
let exchange = project.relate(
|
||||
if layer == Layer::Oa {
|
||||
RelationKind::Interaction
|
||||
} else {
|
||||
RelationKind::FunctionalExchange
|
||||
},
|
||||
"Service",
|
||||
a,
|
||||
work,
|
||||
);
|
||||
let owner = project
|
||||
.add_element(Kind::Scenario, layer, "Lifecycle", None)
|
||||
.unwrap();
|
||||
let mut lane = |name, represented| {
|
||||
let id = project
|
||||
.add_element(Kind::Lifeline, layer, name, Some(owner))
|
||||
.unwrap();
|
||||
*project.elements.get_mut(&id).unwrap().shared_mut().unwrap() = SharedBody::Lifeline {
|
||||
represented: Some(represented),
|
||||
};
|
||||
id
|
||||
};
|
||||
let sender = lane("Requester", a);
|
||||
let receiver = lane("Provider", work);
|
||||
Self {
|
||||
project,
|
||||
owner,
|
||||
sender,
|
||||
receiver,
|
||||
work,
|
||||
exchange,
|
||||
}
|
||||
}
|
||||
fn occurrence(&mut self, parent: Id, name: &str, body: OccurrenceBody) -> Id {
|
||||
let mut element = self
|
||||
.project
|
||||
.make_element(
|
||||
Kind::Occurrence,
|
||||
self.project.elements[&parent].layer(),
|
||||
name,
|
||||
Some(parent),
|
||||
)
|
||||
.unwrap();
|
||||
let id = element.id;
|
||||
*element.shared_mut().unwrap() = SharedBody::Occurrence {
|
||||
body,
|
||||
precondition: String::new(),
|
||||
postcondition: String::new(),
|
||||
};
|
||||
self.project.elements.insert(id, element);
|
||||
let parent = self.project.elements.get_mut(&parent).unwrap();
|
||||
if let Some(steps) = parent.steps_mut() {
|
||||
steps.push(id);
|
||||
} else if let Some(SharedBody::Occurrence {
|
||||
body: OccurrenceBody::Fragment { branches, .. },
|
||||
..
|
||||
}) = parent.shared_mut()
|
||||
{
|
||||
branches.push(id);
|
||||
} else {
|
||||
panic!("fixture parent must own ordered occurrences or operands");
|
||||
}
|
||||
id
|
||||
}
|
||||
fn message(&mut self, parent: Id, kind: MessageKind) -> Id {
|
||||
self.occurrence(
|
||||
parent,
|
||||
"Message",
|
||||
OccurrenceBody::Exchange {
|
||||
exchange: Some(self.exchange),
|
||||
sender: Some(self.sender),
|
||||
receiver: Some(self.receiver),
|
||||
message_kind: kind,
|
||||
operation: None,
|
||||
reply_to: None,
|
||||
values: vec![],
|
||||
},
|
||||
)
|
||||
}
|
||||
fn fragment(&mut self, parent: Id, operator: FragmentOperator) -> Id {
|
||||
self.occurrence(
|
||||
parent,
|
||||
"Paths",
|
||||
OccurrenceBody::Fragment {
|
||||
operator,
|
||||
branches: vec![],
|
||||
},
|
||||
)
|
||||
}
|
||||
fn branch(&mut self, fragment: Id) -> Id {
|
||||
self.occurrence(
|
||||
fragment,
|
||||
"Operand",
|
||||
OccurrenceBody::Branch {
|
||||
guard: "condition".into(),
|
||||
occurrences: vec![],
|
||||
},
|
||||
)
|
||||
}
|
||||
fn document(&self) -> Document {
|
||||
validate_candidate(&self.project).expect("ordinary core admission must succeed");
|
||||
let mut bundle = source_project::format_bundle(&self.project);
|
||||
for text in bundle.files.values_mut() {
|
||||
*text = format!(
|
||||
"// Retain exact authored CRLF and Unicode Ω\r\n{}",
|
||||
text.replace('\n', "\r\n")
|
||||
);
|
||||
}
|
||||
let document =
|
||||
Document::from_bundle(bundle.clone(), source_project::ProjectLimits::default())
|
||||
.unwrap();
|
||||
assert_eq!(document.project(), &self.project);
|
||||
assert_eq!(document.bundle(), &bundle);
|
||||
document
|
||||
}
|
||||
fn scene(&self) -> Scene {
|
||||
let document = self.document();
|
||||
let before = document.snapshot();
|
||||
let scene = Scene::sequence(document.project(), self.owner, self.owner);
|
||||
assert_eq!(document.project(), &before.decoded.project);
|
||||
assert_eq!(document.bundle(), &before.bundle);
|
||||
assert_eq!(document.revision(), 0);
|
||||
assert!(!document.can_undo() && !document.can_redo() && !document.dirty());
|
||||
assert_eq!(
|
||||
scene.lifecycle,
|
||||
Scene::sequence(document.project(), self.owner, self.owner).lifecycle
|
||||
);
|
||||
assert!(
|
||||
scene
|
||||
.lines
|
||||
.iter()
|
||||
.flat_map(|line| &line.points)
|
||||
.all(|p| p.x.is_finite() && p.y.is_finite())
|
||||
);
|
||||
scene
|
||||
}
|
||||
}
|
||||
|
||||
fn intervals(scene: &Scene, lifeline: Id, scope: Id) -> Vec<(f32, f32, Life)> {
|
||||
scene
|
||||
.lifecycle
|
||||
.lifetimes
|
||||
.iter()
|
||||
.find(|lane| lane.lifeline == lifeline)
|
||||
.unwrap()
|
||||
.intervals
|
||||
.iter()
|
||||
.filter(|i| i.scope == scope)
|
||||
.map(|i| (i.span.start, i.span.end, i.life))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn real_scene_long_header_title_and_detail_keep_names_and_have_bounded_clips() {
|
||||
for layer in [Layer::Oa, Layer::Sa] {
|
||||
for name in [
|
||||
"W".repeat(18),
|
||||
"界".repeat(24),
|
||||
"e\u{301}".repeat(24),
|
||||
"👩\u{200d}🔧".repeat(12),
|
||||
] {
|
||||
let mut f = Fixture::new(layer);
|
||||
f.project.elements.get_mut(&f.receiver).unwrap().name = name.clone();
|
||||
f.project.elements.get_mut(&f.work).unwrap().name = name.clone();
|
||||
let scene = f.scene();
|
||||
let header = scene.boxes.iter().find(|b| b.id == f.receiver).unwrap();
|
||||
assert_eq!(header.title, format!("Lane · {name}"));
|
||||
assert_eq!(header.detail, name);
|
||||
assert_eq!(header.rect.size(), Size::new(204., 70.));
|
||||
assert_eq!(scene.hit(header.rect.center()), Some(f.receiver));
|
||||
// Scroll offset is handled by the existing outer canvas/scrollable;
|
||||
// these are the local drawing frame's coordinates, not screen input.
|
||||
for scale in [0.05, 0.5, 0.512, 0.64, 0.8, 1., 1.953125, 2.] {
|
||||
let clip =
|
||||
crate::behavior::box_text_clip(header.rect, scale, Size::new(2000., 2000.))
|
||||
.unwrap();
|
||||
let world = Rectangle::new(
|
||||
Point::new(clip.x / scale, clip.y / scale),
|
||||
Size::new(clip.width / scale, clip.height / scale),
|
||||
);
|
||||
assert!((world.x - header.rect.x - 10.).abs() < 0.001);
|
||||
assert!((world.y - header.rect.y - 8.).abs() < 0.001);
|
||||
assert!((world.width - 184.).abs() < 0.001);
|
||||
assert!((world.height - 54.).abs() < 0.001);
|
||||
assert!(header.rect.contains(world.position()));
|
||||
assert!(
|
||||
header
|
||||
.rect
|
||||
.contains(Point::new(world.x + world.width, world.y + world.height,))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn box_text_clip_intersects_parent_viewport_and_rejects_empty_interiors() {
|
||||
use crate::behavior::box_text_clip;
|
||||
let card = Rectangle::new(Point::new(100., 200.), Size::new(204., 70.));
|
||||
assert_eq!(
|
||||
box_text_clip(card, 2., Size::new(300., 500.)),
|
||||
Some(Rectangle::new(Point::new(220., 416.), Size::new(80., 84.)))
|
||||
);
|
||||
assert_eq!(
|
||||
box_text_clip(
|
||||
Rectangle::new(Point::new(-20., -10.), card.size()),
|
||||
2.,
|
||||
Size::new(300., 500.),
|
||||
),
|
||||
Some(Rectangle::new(Point::ORIGIN, Size::new(300., 104.)))
|
||||
);
|
||||
for viewport in [Size::new(200., 500.), Size::new(300., 400.), Size::ZERO] {
|
||||
assert_eq!(box_text_clip(card, 2., viewport), None);
|
||||
}
|
||||
for scale in [0., -1., f32::NAN, f32::INFINITY] {
|
||||
assert_eq!(box_text_clip(card, scale, Size::new(1000., 1000.)), None);
|
||||
}
|
||||
for size in [Size::new(20., 70.), Size::new(204., 16.), Size::ZERO] {
|
||||
assert_eq!(
|
||||
box_text_clip(Rectangle::new(Point::ORIGIN, size), 1., card.size()),
|
||||
None
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn real_scene_wide_latin_header_exceeds_old_character_budget() {
|
||||
use iced::advanced::text::{self, Paragraph as _};
|
||||
let mut f = Fixture::new(Layer::Oa);
|
||||
f.project.elements.get_mut(&f.receiver).unwrap().name = "W".repeat(18);
|
||||
f.project.elements.get_mut(&f.work).unwrap().name = "W".repeat(24);
|
||||
let scene = f.scene();
|
||||
let header = scene.boxes.iter().find(|b| b.id == f.receiver).unwrap();
|
||||
for (content, size, divisor) in [(&header.title, 13., 7.), (&header.detail, 11., 6.)] {
|
||||
let label = iced::widget::canvas::Text {
|
||||
content: crate::views::short(content, ((header.rect.width - 25.) / divisor) as usize),
|
||||
size: size.into(),
|
||||
..Default::default()
|
||||
};
|
||||
// Use the same real paragraph/font/shaping defaults as canvas Text.
|
||||
// The native defect survives short(): glyph width, not scalar count,
|
||||
// is what exceeds the card. This is a font witness, not a pixel gate.
|
||||
let paragraph = <iced::Renderer as text::Renderer>::Paragraph::with_text(text::Text {
|
||||
content: label.content.as_str(),
|
||||
bounds: Size::INFINITE,
|
||||
size: label.size,
|
||||
line_height: label.line_height,
|
||||
font: label.font,
|
||||
align_x: label.align_x,
|
||||
align_y: label.align_y,
|
||||
shaping: label.shaping,
|
||||
wrapping: text::Wrapping::default(),
|
||||
});
|
||||
let old_available = header.rect.width - 12.;
|
||||
assert!(
|
||||
paragraph.min_width() > old_available,
|
||||
"wide glyph fixture must overflow the old unbounded card: {} <= {old_available}",
|
||||
paragraph.min_width()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn birth_label_clip_keeps_padded_world_bounds_at_each_scene_scale() {
|
||||
let capsule = Rectangle::new(Point::new(100., 200.), Size::new(140., 28.));
|
||||
for (scale, expected) in [
|
||||
(
|
||||
0.05,
|
||||
Rectangle::new(Point::new(5.4, 10.2), Size::new(6.2, 1.)),
|
||||
),
|
||||
(
|
||||
0.5,
|
||||
Rectangle::new(Point::new(54., 102.), Size::new(62., 10.)),
|
||||
),
|
||||
(
|
||||
1.,
|
||||
Rectangle::new(Point::new(108., 204.), Size::new(124., 20.)),
|
||||
),
|
||||
(
|
||||
2.,
|
||||
Rectangle::new(Point::new(216., 408.), Size::new(248., 40.)),
|
||||
),
|
||||
] {
|
||||
let clip = birth_label_clip(capsule, scale, Size::new(1000., 1000.)).unwrap();
|
||||
for (actual, expected) in [
|
||||
(clip.x, expected.x),
|
||||
(clip.y, expected.y),
|
||||
(clip.width, expected.width),
|
||||
(clip.height, expected.height),
|
||||
] {
|
||||
assert!((actual - expected).abs() < 0.0001);
|
||||
}
|
||||
assert!(capsule.contains(Point::new(clip.x / scale, clip.y / scale)));
|
||||
assert!(capsule.contains(Point::new(
|
||||
(clip.x + clip.width) / scale,
|
||||
(clip.y + clip.height) / scale,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn birth_label_clip_preserves_parent_viewport_bounds() {
|
||||
let capsule = Rectangle::new(Point::new(100., 200.), Size::new(140., 28.));
|
||||
assert_eq!(
|
||||
birth_label_clip(capsule, 2., Size::new(300., 420.)),
|
||||
Some(Rectangle::new(Point::new(216., 408.), Size::new(84., 12.)))
|
||||
);
|
||||
let crossing_origin = Rectangle::new(Point::new(-20., -10.), Size::new(140., 28.));
|
||||
assert_eq!(
|
||||
birth_label_clip(crossing_origin, 2., Size::new(300., 420.)),
|
||||
Some(Rectangle::new(Point::ORIGIN, Size::new(224., 28.)))
|
||||
);
|
||||
for viewport in [Size::new(200., 420.), Size::new(300., 400.), Size::ZERO] {
|
||||
assert_eq!(birth_label_clip(capsule, 2., viewport), None);
|
||||
}
|
||||
for scale in [0., -1., f32::NAN, f32::INFINITY] {
|
||||
assert_eq!(
|
||||
birth_label_clip(capsule, scale, Size::new(1000., 1000.)),
|
||||
None
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn real_scene_empty_and_create_only_use_visible_root_extents_not_insertion_bounds() {
|
||||
for layer in [Layer::Oa, Layer::Sa] {
|
||||
let mut f = Fixture::new(layer);
|
||||
let empty = f.scene();
|
||||
assert_eq!(empty.sequence_bounds[&f.owner], (106., 106.));
|
||||
assert_eq!(
|
||||
intervals(&empty, f.receiver, f.owner),
|
||||
vec![(85., empty.size.height - 20., Life::Alive)]
|
||||
);
|
||||
assert!(empty.lines.is_empty(), "lane guides are not message lines");
|
||||
let create = f.message(f.owner, MessageKind::Create);
|
||||
let scene = f.scene();
|
||||
let birth = scene.rows[&create].1;
|
||||
assert_eq!(birth, 148.);
|
||||
assert_eq!(scene.sequence_bounds[&f.owner], (106., 198.));
|
||||
assert_eq!(
|
||||
intervals(&scene, f.receiver, f.owner),
|
||||
vec![
|
||||
(85., birth, Life::Absent),
|
||||
(birth, scene.size.height - 20., Life::Alive),
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
intervals(&scene, f.sender, f.owner),
|
||||
vec![(85., scene.size.height - 20., Life::Alive)]
|
||||
);
|
||||
assert_eq!(scene.slots[0].y, 106.);
|
||||
assert_eq!(
|
||||
scene.lane_at(Point::new(scene.lanes[&f.receiver], 90.)),
|
||||
Some(f.receiver)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn real_scene_birth_death_markers_align_endpoints_and_win_selection_without_mutating_source() {
|
||||
for layer in [Layer::Oa, Layer::Sa] {
|
||||
let mut f = Fixture::new(layer);
|
||||
let create = f.message(f.owner, MessageKind::Create);
|
||||
f.occurrence(
|
||||
f.owner,
|
||||
"Work",
|
||||
OccurrenceBody::Action {
|
||||
action: Some(f.work),
|
||||
lifeline: Some(f.receiver),
|
||||
},
|
||||
);
|
||||
let delete = f.message(f.owner, MessageKind::Delete);
|
||||
let mut scene = f.scene();
|
||||
let birth = scene.rows[&create].1;
|
||||
let death = scene.rows[&delete].1;
|
||||
assert_eq!(
|
||||
intervals(&scene, f.receiver, f.owner),
|
||||
vec![
|
||||
(85., birth, Life::Absent),
|
||||
(birth, death, Life::Alive),
|
||||
(death, scene.size.height - 20., Life::Absent),
|
||||
]
|
||||
);
|
||||
let capsule = scene
|
||||
.lifecycle
|
||||
.markers
|
||||
.iter()
|
||||
.find(|m| m.event.occurrence == create)
|
||||
.unwrap()
|
||||
.capsule;
|
||||
let line = scene.lines.iter().find(|l| l.id == create).unwrap();
|
||||
let expected_x = if scene.lanes[&f.sender] < scene.lanes[&f.receiver] {
|
||||
capsule.x
|
||||
} else {
|
||||
capsule.x + capsule.width
|
||||
};
|
||||
assert_eq!(line.points.last(), Some(&Point::new(expected_x, birth)));
|
||||
assert!(
|
||||
line.label_at.y + 22. < capsule.y,
|
||||
"message caption clears birth capsule"
|
||||
);
|
||||
let line = scene.lines.iter().find(|l| l.id == delete).unwrap();
|
||||
assert_eq!(
|
||||
line.points.last(),
|
||||
Some(&Point::new(scene.lanes[&f.receiver], death))
|
||||
);
|
||||
for scale in [0.5, 1., 2.] {
|
||||
scene.scale = scale;
|
||||
assert_eq!(
|
||||
scene.hit(Point::new(scene.lanes[&f.receiver], birth)),
|
||||
Some(create)
|
||||
);
|
||||
assert_eq!(
|
||||
scene.hit(Point::new(scene.lanes[&f.receiver], death)),
|
||||
Some(delete)
|
||||
);
|
||||
assert_eq!(
|
||||
scene.hit(Point::new(
|
||||
scene.lanes[&f.receiver],
|
||||
scene.size.height - 25.
|
||||
)),
|
||||
Some(f.receiver)
|
||||
);
|
||||
assert_eq!(
|
||||
scene.hit(Point::new(scene.lanes[&f.receiver], 40.)),
|
||||
Some(f.receiver)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn real_scene_branch_delete_clips_only_its_activation_and_keeps_unanswered_call_visible() {
|
||||
for layer in [Layer::Oa, Layer::Sa] {
|
||||
let mut f = Fixture::new(layer);
|
||||
let call = f.message(f.owner, MessageKind::Call);
|
||||
let fragment = f.fragment(f.owner, FragmentOperator::Alternative);
|
||||
let left = f.branch(fragment);
|
||||
let delete = f.message(left, MessageKind::Delete);
|
||||
let right = f.branch(fragment);
|
||||
let scene = f.scene();
|
||||
let death = scene.rows[&delete].1;
|
||||
let (left_start, left_end) = scene.sequence_bounds[&left];
|
||||
let (right_start, _) = scene.sequence_bounds[&right];
|
||||
let bars: Vec<_> = scene
|
||||
.boxes
|
||||
.iter()
|
||||
.filter(|b| b.id == call && b.rect.width == 8.)
|
||||
.map(|b| b.rect)
|
||||
.collect();
|
||||
assert!(
|
||||
bars.iter()
|
||||
.any(|r| r.y == left_start && r.y + r.height == death)
|
||||
);
|
||||
assert!(
|
||||
!bars
|
||||
.iter()
|
||||
.any(|r| r.y < left_end && r.y + r.height > death && r.y >= left_start)
|
||||
);
|
||||
assert!(
|
||||
bars.iter().any(|r| r.y == right_start && r.height == 26.),
|
||||
"empty exclusive sibling retains its own open activation"
|
||||
);
|
||||
assert_eq!(
|
||||
intervals(&scene, f.receiver, right),
|
||||
vec![(right_start, right_start + 26., Life::Alive)]
|
||||
);
|
||||
let frame_end = scene.boxes.iter().find(|b| b.id == fragment).unwrap().rect;
|
||||
assert!(
|
||||
!bars.iter().any(|r| r.y >= frame_end.y + frame_end.height),
|
||||
"uncertain post-fragment lifetime cannot carry a definite activation bar"
|
||||
);
|
||||
assert!(
|
||||
scene
|
||||
.open_calls
|
||||
.iter()
|
||||
.any(|p| p.y < death && p.y > left_start)
|
||||
);
|
||||
assert!(scene.open_calls.iter().any(|p| p.y > right_start));
|
||||
assert_eq!(
|
||||
intervals(&scene, f.receiver, left),
|
||||
vec![
|
||||
(left_start, death, Life::Alive),
|
||||
(death, left_end, Life::Absent)
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
intervals(&scene, f.receiver, f.owner).last().unwrap().2,
|
||||
Life::Unresolved
|
||||
);
|
||||
assert!(!f.project.elements.values().any(|e| matches!(
|
||||
e.shared(),
|
||||
Some(SharedBody::Occurrence {
|
||||
body: OccurrenceBody::Exchange {
|
||||
message_kind: MessageKind::Reply,
|
||||
..
|
||||
},
|
||||
..
|
||||
})
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn real_scene_unassigned_lanes_and_explicit_incomplete_receiver_keep_real_marker_identity() {
|
||||
for layer in [Layer::Oa, Layer::Sa] {
|
||||
let mut f = Fixture::new(layer);
|
||||
*f.project
|
||||
.elements
|
||||
.get_mut(&f.receiver)
|
||||
.unwrap()
|
||||
.shared_mut()
|
||||
.unwrap() = SharedBody::Lifeline { represented: None };
|
||||
let create = f.message(f.owner, MessageKind::Create);
|
||||
if let Some(SharedBody::Occurrence {
|
||||
body: OccurrenceBody::Exchange { exchange, .. },
|
||||
..
|
||||
}) = f.project.elements.get_mut(&create).unwrap().shared_mut()
|
||||
{
|
||||
*exchange = None;
|
||||
}
|
||||
let scene = f.scene();
|
||||
assert!(
|
||||
!scene.lanes.contains_key(&f.receiver),
|
||||
"authoring eligibility remains separate"
|
||||
);
|
||||
let x = scene.lifecycle.lanes[&f.receiver];
|
||||
assert_eq!(scene.lifecycle.markers[0].lifeline, f.receiver);
|
||||
assert_eq!(
|
||||
scene.hit(Point::new(x, scene.rows[&create].1)),
|
||||
Some(create)
|
||||
);
|
||||
assert_eq!(scene.hit(Point::new(x, 40.)), Some(f.receiver));
|
||||
assert_eq!(
|
||||
scene.hit(Point::new(x, scene.size.height - 25.)),
|
||||
Some(f.receiver)
|
||||
);
|
||||
assert_eq!(scene.lane_at(Point::new(x, 90.)), None);
|
||||
if let Some(SharedBody::Occurrence {
|
||||
body: OccurrenceBody::Exchange {
|
||||
receiver, exchange, ..
|
||||
},
|
||||
..
|
||||
}) = f.project.elements.get_mut(&create).unwrap().shared_mut()
|
||||
{
|
||||
*receiver = None;
|
||||
*exchange = Some(f.exchange);
|
||||
}
|
||||
let missing = f.scene();
|
||||
assert!(
|
||||
missing.lifecycle.markers.is_empty(),
|
||||
"do not infer receiver from global exchange"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn real_scene_minimum_zoom_marker_halos_do_not_steal_adjacent_capsule_or_header_hits() {
|
||||
let mut f = Fixture::new(Layer::Sa);
|
||||
*f.project
|
||||
.elements
|
||||
.get_mut(&f.receiver)
|
||||
.unwrap()
|
||||
.shared_mut()
|
||||
.unwrap() = SharedBody::Lifeline { represented: None };
|
||||
let create = f.message(f.owner, MessageKind::Create);
|
||||
let delete = f.message(f.owner, MessageKind::Delete);
|
||||
for event in [create, delete] {
|
||||
if let Some(SharedBody::Occurrence {
|
||||
body: OccurrenceBody::Exchange { exchange, .. },
|
||||
..
|
||||
}) = f.project.elements.get_mut(&event).unwrap().shared_mut()
|
||||
{
|
||||
*exchange = None;
|
||||
}
|
||||
}
|
||||
let mut scene = f.scene();
|
||||
let x = scene.lifecycle.lanes[&f.receiver];
|
||||
assert_eq!(scene.rows[&delete].1 - scene.rows[&create].1, 92.);
|
||||
for scale in [0.05, 0.1, 0.5, 1., 2.] {
|
||||
scene.scale = scale;
|
||||
assert_eq!(
|
||||
scene.hit(Point::new(x, scene.rows[&create].1)),
|
||||
Some(create)
|
||||
);
|
||||
assert_eq!(
|
||||
scene.hit(Point::new(x, scene.rows[&delete].1)),
|
||||
Some(delete)
|
||||
);
|
||||
assert_eq!(scene.hit(Point::new(x, 40.)), Some(f.receiver));
|
||||
if scale <= 0.1 {
|
||||
for offset in [-25., -15., 15., 25.] {
|
||||
assert_eq!(
|
||||
scene.hit(Point::new(x, scene.rows[&delete].1 + offset)),
|
||||
Some(delete),
|
||||
"a nearby death halo must beat an earlier message's wider tolerance"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn real_scene_minimum_zoom_near_ordinary_message_wins_over_lifecycle_halos() {
|
||||
let mut f = Fixture::new(Layer::Sa);
|
||||
f.message(f.owner, MessageKind::Create);
|
||||
let ordinary = f.message(f.owner, MessageKind::Asynchronous);
|
||||
f.message(f.owner, MessageKind::Delete);
|
||||
let mut scene = f.scene();
|
||||
let x = (scene.lanes[&f.sender] + scene.lanes[&f.receiver]) / 2.;
|
||||
for scale in [0.05, 0.1] {
|
||||
scene.scale = scale;
|
||||
assert_eq!(
|
||||
scene.hit(Point::new(x, scene.rows[&ordinary].1 + 2.)),
|
||||
Some(ordinary)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn real_scene_self_lifecycle_elbows_end_on_recorded_marker_rows() {
|
||||
for layer in [Layer::Oa, Layer::Sa] {
|
||||
for kind in [MessageKind::Create, MessageKind::Delete] {
|
||||
let mut f = Fixture::new(layer);
|
||||
let event = f.message(f.owner, kind);
|
||||
if let Some(SharedBody::Occurrence {
|
||||
body:
|
||||
OccurrenceBody::Exchange {
|
||||
exchange, sender, ..
|
||||
},
|
||||
..
|
||||
}) = f.project.elements.get_mut(&event).unwrap().shared_mut()
|
||||
{
|
||||
*exchange = None;
|
||||
*sender = Some(f.receiver);
|
||||
}
|
||||
let scene = f.scene();
|
||||
let marker = &scene.lifecycle.markers[0];
|
||||
let line = scene.lines.iter().find(|l| l.id == event).unwrap();
|
||||
assert_eq!(line.points.len(), 4);
|
||||
assert_eq!(line.points[0].y, marker.center.y - 24.);
|
||||
assert_eq!(line.points[3].y, marker.center.y);
|
||||
assert_eq!(
|
||||
line.points[3].x,
|
||||
if marker.event.kind == MarkerKind::Birth {
|
||||
marker.capsule.x + marker.capsule.width
|
||||
} else {
|
||||
marker.center.x
|
||||
}
|
||||
);
|
||||
assert!(line.points[1].x > marker.capsule.x + marker.capsule.width);
|
||||
assert_eq!(scene.hit(marker.center), Some(event));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn real_scene_self_delete_caption_clears_the_preceding_action_box() {
|
||||
let mut f = Fixture::new(Layer::Sa);
|
||||
let action = f.occurrence(
|
||||
f.owner,
|
||||
"Finish work",
|
||||
OccurrenceBody::Action {
|
||||
action: Some(f.work),
|
||||
lifeline: Some(f.receiver),
|
||||
},
|
||||
);
|
||||
let delete = f.message(f.owner, MessageKind::Delete);
|
||||
if let Some(SharedBody::Occurrence {
|
||||
body: OccurrenceBody::Exchange {
|
||||
exchange, sender, ..
|
||||
},
|
||||
..
|
||||
}) = f.project.elements.get_mut(&delete).unwrap().shared_mut()
|
||||
{
|
||||
*exchange = None;
|
||||
*sender = Some(f.receiver);
|
||||
}
|
||||
let scene = f.scene();
|
||||
let action = scene.boxes.iter().find(|b| b.id == action).unwrap();
|
||||
let line = scene.lines.iter().find(|l| l.id == delete).unwrap();
|
||||
assert!(line.label_at.y > action.rect.y + action.rect.height);
|
||||
assert!(line.label_at.y + 22. < line.points[0].y);
|
||||
assert_eq!(line.points[3].y, scene.rows[&delete].1);
|
||||
assert_eq!(
|
||||
scene
|
||||
.slots
|
||||
.iter()
|
||||
.find(|s| s.slot.before == Some(delete))
|
||||
.unwrap()
|
||||
.y,
|
||||
scene.rows[&delete].1 - 42.
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn real_scene_conditional_repeated_and_parallel_scopes_do_not_claim_definite_continuation() {
|
||||
for operator in [
|
||||
FragmentOperator::Alternative,
|
||||
FragmentOperator::Optional,
|
||||
FragmentOperator::Loop,
|
||||
FragmentOperator::Parallel,
|
||||
] {
|
||||
let mut f = Fixture::new(Layer::Sa);
|
||||
let fragment = f.fragment(f.owner, operator);
|
||||
let first = f.branch(fragment);
|
||||
let create = f.message(first, MessageKind::Create);
|
||||
let scene = f.scene();
|
||||
assert_eq!(scene.lifecycle.markers[0].event.occurrence, create);
|
||||
assert_eq!(
|
||||
intervals(&scene, f.receiver, f.owner).first().unwrap().2,
|
||||
Life::Unresolved
|
||||
);
|
||||
assert_eq!(
|
||||
intervals(&scene, f.receiver, f.owner).last().unwrap().2,
|
||||
Life::Unresolved
|
||||
);
|
||||
assert!(
|
||||
scene
|
||||
.lifecycle
|
||||
.lifetimes
|
||||
.iter()
|
||||
.find(|l| l.lifeline == f.sender)
|
||||
.unwrap()
|
||||
.intervals
|
||||
.iter()
|
||||
.all(|i| i.life == Life::Alive)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn real_scene_lifecycle_edit_undo_redo_restores_exact_source_and_changed_geometry() {
|
||||
for layer in [Layer::Oa, Layer::Sa] {
|
||||
let mut f = Fixture::new(layer);
|
||||
let create = f.message(f.owner, MessageKind::Create);
|
||||
let mut document = f.document();
|
||||
let before = document.snapshot();
|
||||
let geometry = Scene::sequence(document.project(), f.owner, create).lifecycle;
|
||||
let mut edited = document.project().elements[&create].clone();
|
||||
if let Some(SharedBody::Occurrence {
|
||||
body: OccurrenceBody::Exchange { message_kind, .. },
|
||||
..
|
||||
}) = edited.shared_mut()
|
||||
{
|
||||
*message_kind = MessageKind::Asynchronous;
|
||||
}
|
||||
document
|
||||
.execute(Command::EditElement(create, edited))
|
||||
.unwrap();
|
||||
let after = document.snapshot();
|
||||
let changed = Scene::sequence(document.project(), f.owner, create).lifecycle;
|
||||
assert_ne!(geometry, changed);
|
||||
assert!(changed.markers.is_empty());
|
||||
assert_ne!(before.bundle, after.bundle);
|
||||
assert!(document.undo());
|
||||
assert_eq!(document.project(), &before.decoded.project);
|
||||
assert_eq!(document.bundle(), &before.bundle);
|
||||
assert_eq!(
|
||||
Scene::sequence(document.project(), f.owner, create).lifecycle,
|
||||
geometry
|
||||
);
|
||||
assert!(!document.can_undo() && document.can_redo());
|
||||
assert!(document.redo());
|
||||
assert_eq!(document.project(), &after.decoded.project);
|
||||
assert_eq!(document.bundle(), &after.bundle);
|
||||
assert_eq!(
|
||||
Scene::sequence(document.project(), f.owner, create).lifecycle,
|
||||
changed
|
||||
);
|
||||
assert!(!document.can_redo());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,470 @@
|
||||
use super::*;
|
||||
use oa_core::*;
|
||||
|
||||
struct Fixture {
|
||||
p: Project,
|
||||
owner: Id,
|
||||
a: Id,
|
||||
b: Id,
|
||||
exchange: Id,
|
||||
rows: BTreeMap<Id, (Id, f32)>,
|
||||
scopes: BTreeMap<Id, (f32, f32)>,
|
||||
}
|
||||
impl Fixture {
|
||||
fn new(layer: Layer) -> Self {
|
||||
let mut p = Project::empty();
|
||||
let kind = if layer == Layer::Oa {
|
||||
Kind::Activity
|
||||
} else {
|
||||
Kind::Function
|
||||
};
|
||||
let a = p.add_element(kind, layer, "Request", None).unwrap();
|
||||
let b = p.add_element(kind, layer, "Provide", None).unwrap();
|
||||
let owner = p
|
||||
.add_element(Kind::Scenario, layer, "Lifecycle", None)
|
||||
.unwrap();
|
||||
let exchange = p.relate(
|
||||
if layer == Layer::Oa {
|
||||
RelationKind::Interaction
|
||||
} else {
|
||||
RelationKind::FunctionalExchange
|
||||
},
|
||||
"Service",
|
||||
a,
|
||||
b,
|
||||
);
|
||||
let line = |p: &mut Project, represented| {
|
||||
let id = p
|
||||
.add_element(Kind::Lifeline, layer, "Participant", Some(owner))
|
||||
.unwrap();
|
||||
*p.elements.get_mut(&id).unwrap().shared_mut().unwrap() = SharedBody::Lifeline {
|
||||
represented: Some(represented),
|
||||
};
|
||||
id
|
||||
};
|
||||
let a = line(&mut p, a);
|
||||
let b = line(&mut p, b);
|
||||
Self {
|
||||
p,
|
||||
owner,
|
||||
a,
|
||||
b,
|
||||
exchange,
|
||||
rows: BTreeMap::new(),
|
||||
scopes: [(owner, (0., 600.))].into(),
|
||||
}
|
||||
}
|
||||
fn occurrence(&mut self, owner: Id, y: f32, body: OccurrenceBody) -> Id {
|
||||
let mut e = self
|
||||
.p
|
||||
.make_element(
|
||||
Kind::Occurrence,
|
||||
self.p.elements[&owner].layer(),
|
||||
"Occurrence",
|
||||
Some(owner),
|
||||
)
|
||||
.unwrap();
|
||||
let id = e.id;
|
||||
*e.shared_mut().unwrap() = SharedBody::Occurrence {
|
||||
body,
|
||||
precondition: String::new(),
|
||||
postcondition: String::new(),
|
||||
};
|
||||
self.p.elements.insert(id, e);
|
||||
if let Some(steps) = self.p.elements.get_mut(&owner).unwrap().steps_mut() {
|
||||
steps.push(id);
|
||||
} else if let Some(SharedBody::Occurrence {
|
||||
body: OccurrenceBody::Fragment { branches, .. },
|
||||
..
|
||||
}) = self.p.elements.get_mut(&owner).unwrap().shared_mut()
|
||||
{
|
||||
branches.push(id);
|
||||
}
|
||||
self.rows.insert(id, (owner, y));
|
||||
id
|
||||
}
|
||||
fn message(&mut self, owner: Id, y: f32, kind: MessageKind) -> Id {
|
||||
self.occurrence(
|
||||
owner,
|
||||
y,
|
||||
OccurrenceBody::Exchange {
|
||||
exchange: Some(self.exchange),
|
||||
sender: Some(self.a),
|
||||
receiver: Some(self.b),
|
||||
message_kind: kind,
|
||||
operation: None,
|
||||
reply_to: None,
|
||||
values: vec![],
|
||||
},
|
||||
)
|
||||
}
|
||||
fn fragment(&mut self, owner: Id, y: f32, operator: FragmentOperator) -> Id {
|
||||
self.occurrence(
|
||||
owner,
|
||||
y,
|
||||
OccurrenceBody::Fragment {
|
||||
operator,
|
||||
branches: vec![],
|
||||
},
|
||||
)
|
||||
}
|
||||
fn branch(&mut self, fragment: Id, start: f32, end: f32) -> Id {
|
||||
let id = self.occurrence(
|
||||
fragment,
|
||||
start,
|
||||
OccurrenceBody::Branch {
|
||||
guard: "condition".into(),
|
||||
occurrences: vec![],
|
||||
},
|
||||
);
|
||||
self.scopes.insert(id, (start, end));
|
||||
id
|
||||
}
|
||||
fn project(&self) -> Vec<LaneLifetime> {
|
||||
let errors: Vec<_> = validation::validate(&self.p)
|
||||
.into_iter()
|
||||
.filter(|i| i.severity == validation::Severity::Error)
|
||||
.collect();
|
||||
assert!(
|
||||
errors.is_empty(),
|
||||
"fixture must be ordinarily admitted: {errors:?}"
|
||||
);
|
||||
let before = self.p.clone();
|
||||
let source = source_project::format_bundle(&self.p);
|
||||
let geometry = Geometry {
|
||||
rows: &self.rows,
|
||||
scopes: &self.scopes,
|
||||
};
|
||||
let result = project_lifetimes(&self.p, self.owner, &geometry);
|
||||
assert_eq!(result, project_lifetimes(&self.p, self.owner, &geometry));
|
||||
assert_eq!(self.p, before);
|
||||
assert_eq!(source_project::format_bundle(&self.p), source);
|
||||
assert!(
|
||||
result
|
||||
.windows(2)
|
||||
.all(|pair| pair[0].lifeline < pair[1].lifeline)
|
||||
);
|
||||
result
|
||||
}
|
||||
fn lane(&self) -> LaneLifetime {
|
||||
self.project()
|
||||
.into_iter()
|
||||
.find(|lane| lane.lifeline == self.b)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
fn intervals(lane: &LaneLifetime, scope: Id) -> Vec<(f32, f32, Life)> {
|
||||
lane.intervals
|
||||
.iter()
|
||||
.filter(|i| i.scope == scope)
|
||||
.map(|i| (i.span.start, i.span.end, i.life))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn root_lifetime_has_exact_birth_death_boundaries_without_source_mutation() {
|
||||
for layer in [Layer::Oa, Layer::Sa] {
|
||||
let mut f = Fixture::new(layer);
|
||||
let create = f.message(f.owner, 100., MessageKind::Create);
|
||||
f.message(f.owner, 200., MessageKind::Asynchronous);
|
||||
let delete = f.message(f.owner, 300., MessageKind::Delete);
|
||||
let lane = f.lane();
|
||||
assert_eq!(
|
||||
intervals(&lane, f.owner),
|
||||
vec![
|
||||
(0., 100., Life::Absent),
|
||||
(100., 300., Life::Alive),
|
||||
(300., 600., Life::Absent)
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
lane.markers,
|
||||
vec![
|
||||
Marker {
|
||||
occurrence: create,
|
||||
scope: f.owner,
|
||||
y: 100.,
|
||||
kind: MarkerKind::Birth
|
||||
},
|
||||
Marker {
|
||||
occurrence: delete,
|
||||
scope: f.owner,
|
||||
y: 300.,
|
||||
kind: MarkerKind::Death
|
||||
},
|
||||
]
|
||||
);
|
||||
let sender = f
|
||||
.project()
|
||||
.into_iter()
|
||||
.find(|lane| lane.lifeline == f.a)
|
||||
.unwrap();
|
||||
assert_eq!(intervals(&sender, f.owner), vec![(0., 600., Life::Alive)]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alternative_death_does_not_kill_its_displayed_sibling() {
|
||||
let mut f = Fixture::new(Layer::Sa);
|
||||
let fragment = f.fragment(f.owner, 100., FragmentOperator::Alternative);
|
||||
let left = f.branch(fragment, 120., 250.);
|
||||
let right = f.branch(fragment, 300., 450.);
|
||||
f.message(left, 200., MessageKind::Delete);
|
||||
f.message(right, 350., MessageKind::Asynchronous);
|
||||
let lane = f.lane();
|
||||
assert_eq!(
|
||||
intervals(&lane, left),
|
||||
vec![(120., 200., Life::Alive), (200., 250., Life::Absent)]
|
||||
);
|
||||
assert_eq!(intervals(&lane, right), vec![(300., 450., Life::Alive)]);
|
||||
assert_eq!(
|
||||
intervals(&lane, f.owner),
|
||||
vec![(0., 100., Life::Alive), (450., 600., Life::Unresolved)]
|
||||
);
|
||||
assert_eq!(
|
||||
lane.uncertainties,
|
||||
vec![Uncertainty {
|
||||
scope: fragment,
|
||||
reason: UncertaintyReason::Conditional
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn conditional_birth_does_not_infer_absence_in_siblings_or_ancestors() {
|
||||
for operator in [FragmentOperator::Alternative, FragmentOperator::Optional] {
|
||||
let mut f = Fixture::new(Layer::Oa);
|
||||
let fragment = f.fragment(f.owner, 100., operator);
|
||||
let left = f.branch(fragment, 120., 250.);
|
||||
let right = f.branch(fragment, 300., 450.);
|
||||
f.message(left, 200., MessageKind::Create);
|
||||
let lane = f.lane();
|
||||
assert_eq!(
|
||||
intervals(&lane, left),
|
||||
vec![(120., 200., Life::Absent), (200., 250., Life::Alive)]
|
||||
);
|
||||
assert_eq!(
|
||||
intervals(&lane, right),
|
||||
vec![(300., 450., Life::Unresolved)]
|
||||
);
|
||||
assert_eq!(
|
||||
intervals(&lane, f.owner),
|
||||
vec![(0., 100., Life::Unresolved), (450., 600., Life::Unresolved)]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_lifetime_is_local_and_warning_bearing_loops_remain_drawable() {
|
||||
for balanced in [true, false] {
|
||||
let mut f = Fixture::new(Layer::Sa);
|
||||
let fragment = f.fragment(f.owner, 100., FragmentOperator::Loop);
|
||||
let iteration = f.branch(fragment, 120., 450.);
|
||||
let create = f.message(iteration, 160., MessageKind::Create);
|
||||
f.message(iteration, 250., MessageKind::Asynchronous);
|
||||
if balanced {
|
||||
f.message(iteration, 350., MessageKind::Delete);
|
||||
}
|
||||
let lane = f.lane();
|
||||
assert_eq!(lane.markers.len(), if balanced { 2 } else { 1 });
|
||||
assert_eq!(lane.markers[0].occurrence, create);
|
||||
assert!(lane.uncertainties.contains(&Uncertainty {
|
||||
scope: fragment,
|
||||
reason: UncertaintyReason::Repeated
|
||||
}));
|
||||
assert_eq!(
|
||||
intervals(&lane, f.owner).last().unwrap().2,
|
||||
Life::Unresolved
|
||||
);
|
||||
assert_eq!(
|
||||
validation::validate(&f.p)
|
||||
.iter()
|
||||
.any(|i| i.code == "lifeline_repetition"),
|
||||
!balanced
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optional_cleanup_inside_loop_does_not_claim_balanced_or_definite_exit() {
|
||||
let mut f = Fixture::new(Layer::Sa);
|
||||
let repeated = f.fragment(f.owner, 100., FragmentOperator::Loop);
|
||||
let iteration = f.branch(repeated, 120., 500.);
|
||||
f.message(iteration, 160., MessageKind::Create);
|
||||
let optional = f.fragment(iteration, 280., FragmentOperator::Optional);
|
||||
let cleanup = f.branch(optional, 320., 400.);
|
||||
f.message(cleanup, 350., MessageKind::Delete);
|
||||
let lane = f.lane();
|
||||
assert_eq!(
|
||||
intervals(&lane, cleanup),
|
||||
vec![(320., 350., Life::Alive), (350., 400., Life::Absent)]
|
||||
);
|
||||
assert_eq!(
|
||||
intervals(&lane, iteration),
|
||||
vec![
|
||||
(120., 160., Life::Absent),
|
||||
(160., 280., Life::Alive),
|
||||
(400., 500., Life::Unresolved)
|
||||
]
|
||||
);
|
||||
assert!(
|
||||
validation::validate(&f.p)
|
||||
.iter()
|
||||
.any(|i| i.code == "lifeline_repetition")
|
||||
);
|
||||
assert!(lane.uncertainties.contains(&Uncertainty {
|
||||
scope: optional,
|
||||
reason: UncertaintyReason::Conditional
|
||||
}));
|
||||
assert!(lane.uncertainties.contains(&Uncertainty {
|
||||
scope: repeated,
|
||||
reason: UncertaintyReason::Repeated
|
||||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lifecycle_free_fragments_do_not_poison_other_lanes() {
|
||||
let mut f = Fixture::new(Layer::Oa);
|
||||
let fragment = f.fragment(f.owner, 100., FragmentOperator::Parallel);
|
||||
let left = f.branch(fragment, 120., 250.);
|
||||
let right = f.branch(fragment, 300., 450.);
|
||||
f.message(left, 200., MessageKind::Delete);
|
||||
let result = f.project();
|
||||
let sender = result.iter().find(|lane| lane.lifeline == f.a).unwrap();
|
||||
assert!(sender.intervals.iter().all(|i| i.life == Life::Alive));
|
||||
assert!(sender.uncertainties.is_empty());
|
||||
let target = result.iter().find(|lane| lane.lifeline == f.b).unwrap();
|
||||
assert!(target.uncertainties.contains(&Uncertainty {
|
||||
scope: fragment,
|
||||
reason: UncertaintyReason::Parallel
|
||||
}));
|
||||
assert_eq!(intervals(target, right), vec![(300., 450., Life::Alive)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_receiver_is_authoritative_even_with_missing_exchange() {
|
||||
let mut f = Fixture::new(Layer::Oa);
|
||||
let create = f.message(f.owner, 100., MessageKind::Create);
|
||||
if let Some(SharedBody::Occurrence {
|
||||
body: OccurrenceBody::Exchange { exchange, .. },
|
||||
..
|
||||
}) = f.p.elements.get_mut(&create).unwrap().shared_mut()
|
||||
{
|
||||
*exchange = None;
|
||||
}
|
||||
assert_eq!(f.lane().markers[0].occurrence, create);
|
||||
if let Some(SharedBody::Occurrence {
|
||||
body: OccurrenceBody::Exchange {
|
||||
receiver, exchange, ..
|
||||
},
|
||||
..
|
||||
}) = f.p.elements.get_mut(&create).unwrap().shared_mut()
|
||||
{
|
||||
*receiver = None;
|
||||
*exchange = Some(f.exchange);
|
||||
}
|
||||
assert!(
|
||||
f.lane().markers.is_empty(),
|
||||
"exchange endpoints never invent a receiver"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_geometry_is_reported_without_nonfinite_or_guessed_markers() {
|
||||
let mut f = Fixture::new(Layer::Oa);
|
||||
let create = f.message(f.owner, 100., MessageKind::Create);
|
||||
f.rows.insert(create, (f.owner, f32::NAN));
|
||||
let lane = f.lane();
|
||||
assert!(lane.markers.is_empty());
|
||||
assert_eq!(
|
||||
lane.uncertainties,
|
||||
vec![Uncertainty {
|
||||
scope: create,
|
||||
reason: UncertaintyReason::MissingGeometry
|
||||
}]
|
||||
);
|
||||
assert_eq!(
|
||||
intervals(&lane, f.owner),
|
||||
vec![(0., 600., Life::Unresolved)]
|
||||
);
|
||||
f.scopes.remove(&f.owner);
|
||||
let lane = f.lane();
|
||||
assert!(lane.intervals.is_empty());
|
||||
assert_eq!(lane.uncertainties[0].scope, f.owner);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scenario_use_is_opaque_and_never_imports_callee_lifecycle_events() {
|
||||
let mut f = Fixture::new(Layer::Oa);
|
||||
let callee =
|
||||
f.p.add_element(Kind::Scenario, Layer::Oa, "Callee", None)
|
||||
.unwrap();
|
||||
let remote_a =
|
||||
f.p.add_element(Kind::Lifeline, Layer::Oa, "Remote A", Some(callee))
|
||||
.unwrap();
|
||||
let remote_b =
|
||||
f.p.add_element(Kind::Lifeline, Layer::Oa, "Remote B", Some(callee))
|
||||
.unwrap();
|
||||
for (remote, local) in [(remote_a, f.a), (remote_b, f.b)] {
|
||||
let body = f.p.elements[&local].shared().unwrap().clone();
|
||||
*f.p.elements.get_mut(&remote).unwrap().shared_mut().unwrap() = body;
|
||||
}
|
||||
f.occurrence(
|
||||
callee,
|
||||
200.,
|
||||
OccurrenceBody::Exchange {
|
||||
exchange: Some(f.exchange),
|
||||
sender: Some(remote_a),
|
||||
receiver: Some(remote_b),
|
||||
message_kind: MessageKind::Create,
|
||||
operation: None,
|
||||
reply_to: None,
|
||||
values: vec![],
|
||||
},
|
||||
);
|
||||
f.occurrence(
|
||||
f.owner,
|
||||
100.,
|
||||
OccurrenceBody::ScenarioUse {
|
||||
scenario: Some(callee),
|
||||
bindings: vec![
|
||||
LifelineBinding {
|
||||
referenced: remote_a,
|
||||
local: f.a,
|
||||
},
|
||||
LifelineBinding {
|
||||
referenced: remote_b,
|
||||
local: f.b,
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
let lane = f.lane();
|
||||
assert_eq!(intervals(&lane, f.owner), vec![(0., 600., Life::Alive)]);
|
||||
assert!(lane.markers.is_empty());
|
||||
assert!(lane.uncertainties.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn projection_retains_exact_authored_source_and_does_not_enter_history() {
|
||||
use crate::document::Document;
|
||||
let mut f = Fixture::new(Layer::Oa);
|
||||
f.message(f.owner, 100., MessageKind::Create);
|
||||
f.message(f.owner, 300., MessageKind::Delete);
|
||||
let mut source = source_project::format_bundle(&f.p);
|
||||
let text = source.files.get_mut(&source.entry).unwrap();
|
||||
*text = format!("// Retained author note — 雨\r\n{text}\n");
|
||||
let document =
|
||||
Document::from_bundle(source.clone(), source_project::ProjectLimits::default()).unwrap();
|
||||
let accepted = document.project().clone();
|
||||
let geometry = Geometry {
|
||||
rows: &f.rows,
|
||||
scopes: &f.scopes,
|
||||
};
|
||||
let result = project_lifetimes(document.project(), f.owner, &geometry);
|
||||
assert_eq!(result, f.project());
|
||||
assert_eq!(document.bundle(), &source);
|
||||
assert_eq!(document.project(), &accepted);
|
||||
assert!(!document.can_undo());
|
||||
assert!(!document.can_redo());
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,6 +38,7 @@ impl Tool {
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Event {
|
||||
Source(super::source_draft::Event),
|
||||
Start(Id, Tool, Option<Id>),
|
||||
Cancel,
|
||||
Submit,
|
||||
@@ -90,15 +91,22 @@ pub struct State {
|
||||
selected_context: Option<oa_core::topology_context::ContextAddress>,
|
||||
shelf: Option<Shelf>,
|
||||
}
|
||||
#[derive(Default, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, Default, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct Browser {
|
||||
search: String,
|
||||
page: usize,
|
||||
}
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct Draft {
|
||||
#[serde(default)]
|
||||
source: super::source_draft::State,
|
||||
#[serde(default = "Id::new_v4")]
|
||||
invocation: Id,
|
||||
#[serde(default)]
|
||||
source_origin: Option<(Id, oa_core::Element)>,
|
||||
|
||||
#[serde(with = "recovery_insertion")]
|
||||
insertion: Insertion,
|
||||
fixed: bool,
|
||||
@@ -117,6 +125,28 @@ struct Draft {
|
||||
contextual: Option<contextual::Draft>,
|
||||
}
|
||||
impl State {
|
||||
pub(super) fn pending_name(&self) -> Option<&str> {
|
||||
self.draft.as_ref().map(|d| d.name.as_str())
|
||||
}
|
||||
pub(super) fn source_view<'a>(&'a self, p: &'a Project) -> Option<iced::Element<'a, Event>> {
|
||||
self.draft
|
||||
.as_ref()?
|
||||
.source
|
||||
.view(p)
|
||||
.map(|v| v.map(Event::Source))
|
||||
}
|
||||
|
||||
pub fn source_active(&self) -> bool {
|
||||
self.draft.as_ref().is_some_and(|d| d.source.engaged())
|
||||
}
|
||||
|
||||
pub fn source_open(&self) -> bool {
|
||||
self.draft.as_ref().is_some_and(|d| d.source.is_open())
|
||||
}
|
||||
pub fn source_back(&mut self) -> bool {
|
||||
self.draft.as_mut().is_some_and(|d| d.source.back())
|
||||
}
|
||||
|
||||
pub(super) fn recovery_snapshot(&self) -> Result<Option<serde_json::Value>, String> {
|
||||
self.draft
|
||||
.as_ref()
|
||||
@@ -152,6 +182,58 @@ impl State {
|
||||
}
|
||||
|
||||
pub fn update(&mut self, p: &Project, event: Event) -> Result<Option<Command>, String> {
|
||||
if let Event::Source(event) = event {
|
||||
if self
|
||||
.draft
|
||||
.as_mut()
|
||||
.ok_or("Start the consumer form first.")?
|
||||
.source
|
||||
.dismiss(&event)?
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
let d = self
|
||||
.draft
|
||||
.as_ref()
|
||||
.ok_or("Start the consumer form first.")?;
|
||||
if d.tool != Tool::Work {
|
||||
return Err("This reference does not support staged source creation.".into());
|
||||
}
|
||||
if d.source_origin.as_ref().is_some_and(|(document, owner)| {
|
||||
*document != p.id || p.elements.get(&d.owner) != Some(owner)
|
||||
}) {
|
||||
return Err("The original consumer changed. Cancel and reopen its form; the current draft is retained.".into());
|
||||
}
|
||||
if d.source_origin.is_none() && d.source.engaged() {
|
||||
return Err("The recovered source draft has no original document binding. Go Back or cancel it.".into());
|
||||
}
|
||||
let mut next = d.clone();
|
||||
if next.source_origin.is_none() && matches!(&event, super::source_draft::Event::Open(_))
|
||||
{
|
||||
next.source_origin = Some((
|
||||
p.id,
|
||||
p.elements
|
||||
.get(&d.owner)
|
||||
.ok_or("The consumer owner no longer exists.")?
|
||||
.clone(),
|
||||
));
|
||||
}
|
||||
if let Some(id) = next.source.update(
|
||||
p,
|
||||
super::source_draft::Scope::Work { owner: d.owner },
|
||||
d.invocation,
|
||||
event,
|
||||
)? {
|
||||
next.target = Some(id);
|
||||
next.command(p)?;
|
||||
}
|
||||
self.draft = Some(next);
|
||||
return Ok(None);
|
||||
}
|
||||
if self.source_open() && !matches!(&event, Event::Cancel) {
|
||||
return Err("Use the new source or go Back before editing the parent form.".into());
|
||||
}
|
||||
|
||||
if self.draft.is_some() && matches!(&event, Event::Start(..)) {
|
||||
return Err(PENDING_DRAFT.into());
|
||||
}
|
||||
@@ -184,6 +266,9 @@ impl State {
|
||||
}
|
||||
});
|
||||
self.draft = Some(Draft {
|
||||
source: Default::default(),
|
||||
invocation: Id::new_v4(),
|
||||
source_origin: Some((p.id, p.elements[&owner].clone())),
|
||||
insertion: Insertion::default(),
|
||||
fixed: false,
|
||||
browsers: Default::default(),
|
||||
@@ -508,6 +593,22 @@ fn link(p: &Project, source: Id, target: Id, guard: &str) -> Command {
|
||||
}
|
||||
impl Draft {
|
||||
fn command(&self, p: &Project) -> Result<Command, String> {
|
||||
if self.source.engaged()
|
||||
&& self.source_origin.as_ref().is_none_or(|(document, owner)| {
|
||||
*document != p.id || p.elements.get(&self.owner) != Some(owner)
|
||||
})
|
||||
{
|
||||
return Err("The original consumer document or owner changed. Cancel and reopen its form; your draft is retained.".into());
|
||||
}
|
||||
self.source.check_scope(
|
||||
super::source_draft::Scope::Work { owner: self.owner },
|
||||
self.target,
|
||||
)?;
|
||||
let preview = self.source.preview(p, self.target)?;
|
||||
let command = self.consumer_command(preview.as_ref().unwrap_or(p))?;
|
||||
self.source.combine(p, self.target, command)
|
||||
}
|
||||
fn consumer_command(&self, p: &Project) -> Result<Command, String> {
|
||||
let owner = p
|
||||
.elements
|
||||
.get(&self.owner)
|
||||
@@ -615,6 +716,8 @@ impl Draft {
|
||||
});
|
||||
let name = if self.name.trim().is_empty() {
|
||||
default_name
|
||||
} else if self.source.label(self.target).is_some() {
|
||||
self.name.as_str()
|
||||
} else {
|
||||
self.name.trim()
|
||||
};
|
||||
@@ -1587,6 +1690,9 @@ mod tests {
|
||||
}
|
||||
fn branch(owner: Id, a: Id, b: Id, before: Id, after: Id) -> Draft {
|
||||
Draft {
|
||||
source: Default::default(),
|
||||
invocation: Id::new_v4(),
|
||||
source_origin: None,
|
||||
insertion: Insertion::default(),
|
||||
fixed: false,
|
||||
browsers: Default::default(),
|
||||
|
||||
@@ -33,7 +33,7 @@ pub enum Event {
|
||||
Search(String),
|
||||
Page(usize),
|
||||
}
|
||||
#[derive(Default, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, Default, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub(super) struct Draft {
|
||||
edit: Option<Id>,
|
||||
|
||||
@@ -206,8 +206,16 @@ impl State {
|
||||
}
|
||||
None
|
||||
}
|
||||
pub(crate) fn created_selection(&self, command: &Command) -> Option<Id> {
|
||||
pub(crate) fn created_selection(&self, p: &Project, command: &Command) -> Option<Id> {
|
||||
let owner = self.pending_owner()?;
|
||||
if self
|
||||
.draft
|
||||
.as_ref()
|
||||
.is_some_and(|d| d.source.label(d.target).is_some())
|
||||
&& self.prepare_pending(p).ok().as_ref() != Some(command)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
if let Command::CreateRelation(r) | Command::EditRelation(_, r) = command
|
||||
&& matches!(&r.body, RelationBody::ContextualLink { owner: caller, .. } if *caller == owner)
|
||||
{
|
||||
|
||||
@@ -369,6 +369,25 @@ pub(super) fn view<'a>(app: &'a App, owner: Id, out: Column<'a, Message>) -> Ele
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.style(style::card);
|
||||
if !pending && !p.children(owner).any(|e| is_contribution(p, owner, e.id)) {
|
||||
let title = if p.layer_of(owner) == Some(Layer::Oa) {
|
||||
"Add first activity contribution"
|
||||
} else {
|
||||
"Add first function contribution"
|
||||
};
|
||||
header = header.push(button(title).on_press_maybe(
|
||||
(!app.behavior.has_pending() && !app.draft_dirty() && !app.source_dirty()).then(
|
||||
|| {
|
||||
Message::TopologyCanvas(CanvasEvent::Begin {
|
||||
context,
|
||||
tool: Tool::Work,
|
||||
control: ControlKind::Choice,
|
||||
insertion: Insertion::default(),
|
||||
})
|
||||
},
|
||||
),
|
||||
));
|
||||
}
|
||||
let details: Element<'_, Message> = match state.shelf {
|
||||
Some(Shelf::Tools) => controls.into(),
|
||||
_ => navigation.push(text("Full qualified identities and definition actions appear here for the selected contribution or link.").size(11)).into(),
|
||||
@@ -383,201 +402,223 @@ pub(super) fn view<'a>(app: &'a App, owner: Id, out: Column<'a, Message>) -> Ele
|
||||
};
|
||||
let mut form_panel: Element<'_, Message> = iced::widget::Space::new().height(0).into();
|
||||
if let Some(d) = state.draft.as_ref().filter(|d| d.owner == owner) {
|
||||
let mut form = column![text(d.tool.title()).size(16)].spacing(8);
|
||||
if let Some(contextual) = &d.contextual {
|
||||
form = form
|
||||
.push(
|
||||
text_input("Link name (retain current when blank)", &d.name)
|
||||
.on_input(|v| event(Event::Name(v)))
|
||||
.padding(8)
|
||||
.size(12),
|
||||
)
|
||||
.push(super::contextual::view(p, owner, contextual));
|
||||
if let Some(child) = d.source.view(p) {
|
||||
form_panel = column![
|
||||
text(format!(
|
||||
"Consumer name: {}",
|
||||
crate::views::short(&d.name, 60)
|
||||
))
|
||||
.size(12),
|
||||
child.map(|e| event(Event::Source(e)))
|
||||
]
|
||||
.spacing(6)
|
||||
.height(Fill)
|
||||
.into();
|
||||
} else {
|
||||
if let Some(link) = d.insertion.link.and_then(|id| p.relations.get(&id)) {
|
||||
let guard = if let RelationBody::SequenceLink { guard, .. } = &link.body {
|
||||
guard.as_str()
|
||||
} else {
|
||||
""
|
||||
};
|
||||
form = form.push(label(format!(
|
||||
"Insert on {} → {} · preserve path identity and condition [{}]",
|
||||
p.named(link.source()),
|
||||
p.named(link.target()),
|
||||
guard
|
||||
)));
|
||||
} else if d.fixed && ordered {
|
||||
form = form.push(label(match (d.before, d.after) {
|
||||
(Some(before), Some(after)) => format!(
|
||||
"Insert between {} → {} · preserve the ordered path",
|
||||
p.named(before),
|
||||
p.named(after)
|
||||
),
|
||||
(Some(before), None) => format!("Continue after {}", p.named(before)),
|
||||
(None, Some(after)) => format!("Insert before {}", p.named(after)),
|
||||
_ => "Start the ordered path".into(),
|
||||
}));
|
||||
} else if d.fixed
|
||||
&& d.tool == Tool::Exchange
|
||||
&& let Some(before) = d.before
|
||||
{
|
||||
form = form.push(label(format!(
|
||||
"Append after {} → new exchange · Create adds an explicit sequence link",
|
||||
p.named(before)
|
||||
)));
|
||||
} else if d.fixed && d.tool != Tool::Connect {
|
||||
form = form.push(label(
|
||||
d.insertion
|
||||
.before
|
||||
.map_or("Append an unconnected contribution".into(), |id| {
|
||||
format!("Unconnected contribution before {}", p.named(id))
|
||||
}),
|
||||
));
|
||||
}
|
||||
if !legacy_order(p, owner).is_empty() {
|
||||
form = form.push(label(
|
||||
let mut form = column![text(d.tool.title()).size(16)].spacing(8);
|
||||
if let Some(contextual) = &d.contextual {
|
||||
form = form
|
||||
.push(
|
||||
text_input("Link name (retain current when blank)", &d.name)
|
||||
.on_input(|v| event(Event::Name(v)))
|
||||
.padding(8)
|
||||
.size(12),
|
||||
)
|
||||
.push(super::contextual::view(p, owner, contextual));
|
||||
} else {
|
||||
if let Some(link) = d.insertion.link.and_then(|id| p.relations.get(&id)) {
|
||||
let guard = if let RelationBody::SequenceLink { guard, .. } = &link.body {
|
||||
guard.as_str()
|
||||
} else {
|
||||
""
|
||||
};
|
||||
form = form.push(label(format!(
|
||||
"Insert on {} → {} · preserve path identity and condition [{}]",
|
||||
p.named(link.source()),
|
||||
p.named(link.target()),
|
||||
guard
|
||||
)));
|
||||
} else if d.fixed && ordered {
|
||||
form = form.push(label(match (d.before, d.after) {
|
||||
(Some(before), Some(after)) => format!(
|
||||
"Insert between {} → {} · preserve the ordered path",
|
||||
p.named(before),
|
||||
p.named(after)
|
||||
),
|
||||
(Some(before), None) => format!("Continue after {}", p.named(before)),
|
||||
(None, Some(after)) => format!("Insert before {}", p.named(after)),
|
||||
_ => "Start the ordered path".into(),
|
||||
}));
|
||||
} else if d.fixed
|
||||
&& d.tool == Tool::Exchange
|
||||
&& let Some(before) = d.before
|
||||
{
|
||||
form = form.push(label(format!(
|
||||
"Append after {} → new exchange · Create adds an explicit sequence link",
|
||||
p.named(before)
|
||||
)));
|
||||
} else if d.fixed && d.tool != Tool::Connect {
|
||||
form = form.push(label(
|
||||
d.insertion
|
||||
.before
|
||||
.map_or("Append an unconnected contribution".into(), |id| {
|
||||
format!("Unconnected contribution before {}", p.named(id))
|
||||
}),
|
||||
));
|
||||
}
|
||||
if d.tool == Tool::Work {
|
||||
if let Some(title) = d.source.label(d.target) {
|
||||
form = form.push(label(title)).push(label("Create commits the new work and contribution together at the chosen path position."));
|
||||
}
|
||||
form = form.push(button("Create new source…").on_press(event(Event::Source(
|
||||
super::super::source_draft::Event::Open(d.invocation),
|
||||
))));
|
||||
}
|
||||
if !legacy_order(p, owner).is_empty() {
|
||||
form = form.push(label(
|
||||
"This edit preserves the ordered path as explicit links in the same undo step.",
|
||||
));
|
||||
}
|
||||
if matches!(
|
||||
d.tool,
|
||||
Tool::Work | Tool::Exchange | Tool::Reuse | Tool::Branch
|
||||
) {
|
||||
let choices = targets(p, owner, d.tool)
|
||||
.into_iter()
|
||||
.filter(|c| compatible_source(p, d, c.id))
|
||||
.collect();
|
||||
if d.tool == Tool::Branch {
|
||||
form = form.push(label("First path work"));
|
||||
}
|
||||
form = form.push(picker(
|
||||
choices,
|
||||
d,
|
||||
0,
|
||||
d.target,
|
||||
if matches!(
|
||||
d.tool,
|
||||
Tool::Work | Tool::Exchange | Tool::Reuse | Tool::Branch
|
||||
) {
|
||||
let choices = targets(p, owner, d.tool)
|
||||
.into_iter()
|
||||
.filter(|c| compatible_source(p, d, c.id))
|
||||
.collect();
|
||||
if d.tool == Tool::Branch {
|
||||
"Choose first path work…"
|
||||
form = form.push(label("First path work"));
|
||||
}
|
||||
form = form.push(picker(
|
||||
choices,
|
||||
d,
|
||||
0,
|
||||
d.target,
|
||||
if d.tool == Tool::Branch {
|
||||
"Choose first path work…"
|
||||
} else {
|
||||
"Choose existing source…"
|
||||
},
|
||||
Event::Target,
|
||||
));
|
||||
}
|
||||
if d.tool != Tool::Connect {
|
||||
form = form.push(
|
||||
text_input("Contribution name (defaults to source)", &d.name)
|
||||
.on_input(|v| event(Event::Name(v)))
|
||||
.padding(8)
|
||||
.size(12),
|
||||
);
|
||||
}
|
||||
if matches!(d.tool, Tool::Control | Tool::Branch) {
|
||||
let kinds = if d.tool == Tool::Branch {
|
||||
vec![ControlKind::Choice, ControlKind::Fork]
|
||||
} else {
|
||||
"Choose existing source…"
|
||||
},
|
||||
Event::Target,
|
||||
));
|
||||
}
|
||||
if d.tool != Tool::Connect {
|
||||
form = form.push(
|
||||
text_input("Contribution name (defaults to source)", &d.name)
|
||||
.on_input(|v| event(Event::Name(v)))
|
||||
vec![
|
||||
ControlKind::Choice,
|
||||
ControlKind::Merge,
|
||||
ControlKind::Fork,
|
||||
ControlKind::Join,
|
||||
ControlKind::Loop,
|
||||
]
|
||||
};
|
||||
form = form.push(
|
||||
pick_list(
|
||||
kinds.into_iter().map(ControlChoice).collect::<Vec<_>>(),
|
||||
Some(ControlChoice(d.control)),
|
||||
|v| event(Event::Control(v)),
|
||||
)
|
||||
.padding(8)
|
||||
.text_size(12),
|
||||
);
|
||||
}
|
||||
if d.tool == Tool::Branch {
|
||||
form = form.push(label("Second path work")).push(picker(
|
||||
targets(p, owner, Tool::Work),
|
||||
d,
|
||||
1,
|
||||
d.alternate,
|
||||
"Second path work…",
|
||||
Event::Alternate,
|
||||
));
|
||||
if d.control != ControlKind::Fork {
|
||||
form = form
|
||||
.push(label("First path guard"))
|
||||
.push(
|
||||
text_input("First path guard", &d.guard)
|
||||
.on_input(|v| event(Event::Guard(v)))
|
||||
.padding(8),
|
||||
)
|
||||
.push(label("Second path guard"))
|
||||
.push(
|
||||
text_input("Second path guard (e.g. else)", &d.alternate_guard)
|
||||
.on_input(|v| event(Event::AlternateGuard(v)))
|
||||
.padding(8),
|
||||
);
|
||||
}
|
||||
}
|
||||
if d.fixed {
|
||||
if let (Some(a), Some(b)) = (d.before, d.after) {
|
||||
form = form.push(label(format!("{} → {}", p.named(a), p.named(b))));
|
||||
}
|
||||
} else {
|
||||
form = form
|
||||
.push(label("Preceding contribution"))
|
||||
.push(picker(
|
||||
contributions(p, owner),
|
||||
d,
|
||||
2,
|
||||
d.before,
|
||||
"After contribution…",
|
||||
Event::Before,
|
||||
))
|
||||
.push(button("Clear preceding").on_press(event(Event::ClearBefore)))
|
||||
.push(label("Following contribution"))
|
||||
.push(picker(
|
||||
contributions(p, owner),
|
||||
d,
|
||||
3,
|
||||
d.after,
|
||||
"Before contribution…",
|
||||
Event::After,
|
||||
))
|
||||
.push(button("Clear following").on_press(event(Event::ClearAfter)));
|
||||
}
|
||||
if d.tool != Tool::Branch
|
||||
&& d.insertion.link.is_none()
|
||||
&& (d.before.is_some() || d.tool == Tool::Connect)
|
||||
{
|
||||
form = form.push(
|
||||
text_input(
|
||||
"Path condition (empty remains a Choice / Loop warning)",
|
||||
&d.guard,
|
||||
)
|
||||
.on_input(|v| event(Event::Guard(v)))
|
||||
.padding(8)
|
||||
.size(12),
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
if matches!(d.tool, Tool::Control | Tool::Branch) {
|
||||
let kinds = if d.tool == Tool::Branch {
|
||||
vec![ControlKind::Choice, ControlKind::Fork]
|
||||
let valid = match d.command(p) {
|
||||
Ok(_) => true,
|
||||
Err(problem) => {
|
||||
form = form.push(label(problem));
|
||||
false
|
||||
}
|
||||
};
|
||||
let actions = row![
|
||||
button(if d.contextual.is_some() {
|
||||
"Apply link"
|
||||
} else {
|
||||
vec![
|
||||
ControlKind::Choice,
|
||||
ControlKind::Merge,
|
||||
ControlKind::Fork,
|
||||
ControlKind::Join,
|
||||
ControlKind::Loop,
|
||||
]
|
||||
};
|
||||
form = form.push(
|
||||
pick_list(
|
||||
kinds.into_iter().map(ControlChoice).collect::<Vec<_>>(),
|
||||
Some(ControlChoice(d.control)),
|
||||
|v| event(Event::Control(v)),
|
||||
)
|
||||
.padding(8)
|
||||
.text_size(12),
|
||||
);
|
||||
}
|
||||
if d.tool == Tool::Branch {
|
||||
form = form.push(label("Second path work")).push(picker(
|
||||
targets(p, owner, Tool::Work),
|
||||
d,
|
||||
1,
|
||||
d.alternate,
|
||||
"Second path work…",
|
||||
Event::Alternate,
|
||||
));
|
||||
if d.control != ControlKind::Fork {
|
||||
form = form
|
||||
.push(label("First path guard"))
|
||||
.push(
|
||||
text_input("First path guard", &d.guard)
|
||||
.on_input(|v| event(Event::Guard(v)))
|
||||
.padding(8),
|
||||
)
|
||||
.push(label("Second path guard"))
|
||||
.push(
|
||||
text_input("Second path guard (e.g. else)", &d.alternate_guard)
|
||||
.on_input(|v| event(Event::AlternateGuard(v)))
|
||||
.padding(8),
|
||||
);
|
||||
}
|
||||
}
|
||||
if d.fixed {
|
||||
if let (Some(a), Some(b)) = (d.before, d.after) {
|
||||
form = form.push(label(format!("{} → {}", p.named(a), p.named(b))));
|
||||
}
|
||||
} else {
|
||||
form = form
|
||||
.push(label("Preceding contribution"))
|
||||
.push(picker(
|
||||
contributions(p, owner),
|
||||
d,
|
||||
2,
|
||||
d.before,
|
||||
"After contribution…",
|
||||
Event::Before,
|
||||
))
|
||||
.push(button("Clear preceding").on_press(event(Event::ClearBefore)))
|
||||
.push(label("Following contribution"))
|
||||
.push(picker(
|
||||
contributions(p, owner),
|
||||
d,
|
||||
3,
|
||||
d.after,
|
||||
"Before contribution…",
|
||||
Event::After,
|
||||
))
|
||||
.push(button("Clear following").on_press(event(Event::ClearAfter)));
|
||||
}
|
||||
if d.tool != Tool::Branch
|
||||
&& d.insertion.link.is_none()
|
||||
&& (d.before.is_some() || d.tool == Tool::Connect)
|
||||
{
|
||||
form = form.push(
|
||||
text_input(
|
||||
"Path condition (empty remains a Choice / Loop warning)",
|
||||
&d.guard,
|
||||
)
|
||||
.on_input(|v| event(Event::Guard(v)))
|
||||
.padding(8)
|
||||
.size(12),
|
||||
);
|
||||
}
|
||||
"Create"
|
||||
})
|
||||
.on_press_maybe(valid.then(|| event(Event::Submit))),
|
||||
button("Cancel").on_press(event(Event::Cancel))
|
||||
]
|
||||
.spacing(7);
|
||||
form_panel = editor_panel(actions.into(), form.into());
|
||||
}
|
||||
let valid = match d.command(p) {
|
||||
Ok(_) => true,
|
||||
Err(problem) => {
|
||||
form = form.push(label(problem));
|
||||
false
|
||||
}
|
||||
};
|
||||
let actions = row![
|
||||
button(if d.contextual.is_some() {
|
||||
"Apply link"
|
||||
} else {
|
||||
"Create"
|
||||
})
|
||||
.on_press_maybe(valid.then(|| event(Event::Submit))),
|
||||
button("Cancel").on_press(event(Event::Cancel))
|
||||
]
|
||||
.spacing(7);
|
||||
form_panel = editor_panel(actions.into(), form.into());
|
||||
}
|
||||
panes(diagram, form_panel, pending)
|
||||
}
|
||||
|
||||
@@ -120,7 +120,9 @@ fn seed(schema: &'static Value, depth: usize) -> Result<Value, &'static str> {
|
||||
if let Some(options) = alternatives(schema) {
|
||||
if options
|
||||
.iter()
|
||||
.any(|s| matches_value(s, &json!("NotApplicable"), 0))
|
||||
.filter_map(|s| resolve(s).ok())
|
||||
.filter_map(|s| s.get("enum").and_then(Value::as_array))
|
||||
.any(|values| values.contains(&json!("NotApplicable")))
|
||||
{
|
||||
return Ok(json!("NotApplicable"));
|
||||
}
|
||||
@@ -577,11 +579,11 @@ fn render<'a>(
|
||||
for (index, item) in items.iter().enumerate().skip(page * PAGE_SIZE).take(PAGE_SIZE) {
|
||||
let item_path = format!("{path}/{index}");
|
||||
let mut card = column![render(draft, project, item_schema, Some(item), &item_path, &format!("{} {}", title, index + 1), depth + 1)].spacing(8);
|
||||
if path == FACETS {
|
||||
if let Some(current) = current_observation(draft, project, &item_path) {
|
||||
card = card.push(button("Use current counterpart observations")
|
||||
.on_press(Message::FieldShape(item_path.clone(), json!(current))).style(style::nav));
|
||||
}
|
||||
if path == FACETS
|
||||
&& let Some(current) = current_observation(draft, project, &item_path)
|
||||
{
|
||||
card = card.push(button("Use current counterpart observations")
|
||||
.on_press(Message::FieldShape(item_path.clone(), json!(current))).style(style::nav));
|
||||
}
|
||||
card = card.push(row![
|
||||
button("↑").on_press_maybe((index > 0).then(|| Message::FieldArray(path.into(), Some(index), -1))),
|
||||
|
||||
@@ -752,6 +752,38 @@ fn choose_history_constructor(draft: &mut Draft, path: &str, label: &str) {
|
||||
draft.shape(&path, value).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn history_optional_uuid_and_text_seed_absence_without_inventing_observations() {
|
||||
for fact in ["Header/Parent", "Artifact/review/Revision"] {
|
||||
let side = format!("{FACETS}/0/{fact}/oa");
|
||||
let schema = resolve(schema_at(&side).unwrap()).unwrap();
|
||||
assert_eq!(seed(schema, 0).unwrap(), json!("NotApplicable"));
|
||||
let observed_value = alternatives(schema)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.find(|branch| branch_label(branch) == "Observed value")
|
||||
.unwrap();
|
||||
let value = branch_seed(observed_value).unwrap();
|
||||
assert_eq!(value, json!({"Value": null}));
|
||||
assert_eq!(
|
||||
seed(schema_at(&format!("{side}/Value")).unwrap(), 0).unwrap(),
|
||||
Value::Null,
|
||||
"a plain text/UUID shape is not the NotApplicable enum constructor"
|
||||
);
|
||||
if fact == "Header/Parent" {
|
||||
assert_eq!(
|
||||
serde_json::from_value::<Observed<Option<Id>>>(value).unwrap(),
|
||||
Observed::Value(None)
|
||||
);
|
||||
} else {
|
||||
assert_eq!(
|
||||
serde_json::from_value::<Observed<Option<String>>>(value).unwrap(),
|
||||
Observed::Value(None)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn history_nested_optional_number_events_switch_absent_present_and_recover_invalid_raw() {
|
||||
let (project, id) = fixture(Some(baseline(vec![])));
|
||||
|
||||
@@ -187,8 +187,8 @@ fn article(topic: Topic) -> Article {
|
||||
&[
|
||||
"Choose New in the top bar. Use Project properties to name the model and describe its purpose. Apply changes commits the property draft.",
|
||||
"On Architecture, choose Create architecture view. Architecture · allocated work shows work inside its performer.",
|
||||
"Choose Entity in the diagram's Create toolbar. Enter Rescue coordinator, choose Choose position, then click the diagram. Use Actor for an external participant such as a Vessel in distress. You can also right-click a blank area, choose Create Entity here, enter its name and choose Create here.",
|
||||
"Choose Activity in the same toolbar, name it Assess incident and place it. Select Rescue coordinator and use + Relationship with Allocation to assign Assess incident to it. The allocated-work view then shows the activity inside its performer.",
|
||||
"Expand the diagram's side palette to see tool names. Choose Entity, click its position in the diagram, enter Rescue coordinator and press Enter. Escape cancels the draft. Use Actor for an external participant such as a Vessel in distress. Right-clicking a blank area also offers Create Entity here.",
|
||||
"Right-click Rescue coordinator and choose Add activity. Enter Assess incident and press Enter to create the activity and its performer allocation together. Ordinary Activity creation in the palette leaves work unallocated; use + Relationship with Allocation to assign existing work. The allocated-work view shows work inside its performer.",
|
||||
"Use Add existing to place an element that is already in the model. The initial choices match this view; no search is required. Include already shown offers Locate for elements already placed here.",
|
||||
"Add a second activity for another participant. Select an activity, choose + Relationship, select Interaction and choose the compatible target. Name the exchange after what is conveyed.",
|
||||
"Visit Model checks to review unfinished work. Save all writes the accepted model and applied drafts to your chosen file.",
|
||||
@@ -262,8 +262,8 @@ fn article(topic: Topic) -> Article {
|
||||
"Create and allocate work",
|
||||
&[
|
||||
"Choose the Operational or System perspective. Create participants using Entity/Actor in OA or System/System actor in SA.",
|
||||
"In the diagram's Create toolbar, choose Activity in OA or Function in SA, enter a name and click to place it. For semantic decomposition, use Model explorer, select existing work and use + In selected to create smaller work units.",
|
||||
"To assign work to a participant, use + Relationship with Allocation from the participant to the leaf work. Model explorer also offers + Allocated when a participant and the appropriate work kind are selected.",
|
||||
"In the diagram's side palette, choose Activity in OA or Function in SA, click its position, enter a name and press Enter. Escape cancels without creating an element. Select a card and press F2 to rename it in place. For semantic decomposition, use Model explorer, select existing work and use + In selected to create smaller work units.",
|
||||
"Right-click a participant and choose Add activity or Add function to create new work and its allocation together. To assign existing leaf work, use + Relationship with Allocation from the participant to the work. Model explorer also offers + Allocated when a participant and the appropriate work kind are selected.",
|
||||
"Choose Architecture · allocated work to review responsibilities. Choose Functional dataflow to review the activity/function hierarchy and its exchanges independently.",
|
||||
],
|
||||
),
|
||||
@@ -445,9 +445,9 @@ fn article(topic: Topic) -> Article {
|
||||
walkthrough(
|
||||
"Describe a path through exchanges",
|
||||
&[
|
||||
"First create the activities/functions and their exchanges. Check the source and target work for every exchange.",
|
||||
"Open Behavior & scenarios. Create Process in OA or Functional chain in SA using Create element and + At root.",
|
||||
"Select that behavior in the page's selector. Open Tools and choose Work or Exchange, then a highlighted path or contribution position to involve an existing source. Each contribution keeps its own identity while referring to the shared engineering definition.",
|
||||
"A blank path offers Add first activity/function contribution. You can use existing work or stage a new source in that form; exchanges still need explicit compatible source and target work.",
|
||||
"Open Behavior & scenarios → New. Choose Operational process in OA or Functional chain in SA, enter its exact name, then Create and open. The new behavior belongs at the layer root and opens immediately; Undo removes the creation as one edit.",
|
||||
"Open Tools and choose Work or Exchange, then a highlighted position. In Work, Create new source keeps the contribution name and position while you name its Activity/Function and choose its semantic parent. Use this new source stages it; Create commits source and contribution in one Undo. An adjacent exchange may require a particular existing work identity.",
|
||||
"In Tools, choose Sequence link and click or drag from the preceding contribution to the following contribution. Choose Tools → Select / reorder to drag a selected path endpoint and reconnect it; a contribution's ↕ handle changes its position within a column. These are local contributions and sequence links, not new architecture exchanges.",
|
||||
"Select a contribution, then add another after it. Tools → Two paths… creates a split, two paths and their merge in one undoable edit. Enter explicit alternative conditions. Fit overview uses the visible canvas area; 100% keeps working scale with scrollbars. If the minimum zoom is reached, scroll to inspect the remainder.",
|
||||
"Use Chain involvement from the Process/Functional chain to the capability it supports. Read Model checks for unfinished or disconnected paths.",
|
||||
@@ -484,28 +484,36 @@ fn article(topic: Topic) -> Article {
|
||||
const {
|
||||
Article {
|
||||
summary: "Use a scenario to tell a concrete behavioral story, including repeated messages, actions, state observations and guarded alternatives.",
|
||||
keywords: "scenario sequence lifeline sender receiver fragment branch alternative loop guard parallel strict optional occurrence exchange action state interface operation allocation call reply unresolved",
|
||||
keywords: "scenario sequence lifeline lifetime lifecycle birth death capsule sender receiver fragment branch alternative loop guard parallel strict optional occurrence exchange action state interface operation allocation call reply unresolved",
|
||||
sections: &[
|
||||
walkthrough(
|
||||
"Create a scenario",
|
||||
&[
|
||||
"Open Behavior & scenarios, choose Scenario under Create element and add it at a legal root/owner. Select the scenario in the behavior selector.",
|
||||
"Use + Lifeline and choose the participant or work it represents. Choices follow the scenario's viewpoint: operational activities, operational participants, system functions or system boundary participants.",
|
||||
"Choose + Message and its style. Call + correlated reply creates both occurrences in one edit. Select the exchange and compatible sending/receiving lifelines. Reply to existing call offers valid unanswered calls.",
|
||||
"Use Action, Observe state or Fragment for other behavior. A local timer has an expression and a lifeline, with no global exchange. Create and delete messages describe participant lifetime; Model checks also flag risks inside repeated loops.",
|
||||
"Choose a message style and Draw message. Start at a highlighted sequence position on a lifeline, then click or drag to the receiving lifeline. The form retains the chosen position while you select the exchange and details. Escape cancels the gesture.",
|
||||
"Open Behavior & scenarios → New → Scenario and enter its name. Explicitly choose Activities or Participants in OA, or Functions or System boundary in SA. Add optional context only when needed: this is a reference, while the scenario itself belongs at the layer root. Create and open starts the empty scenario.",
|
||||
"Use Add first lifeline on an empty scenario, or + Lifeline. Choose existing work/participant or Create new source. The explicit viewpoint limits its kinds; choose participant kinds deliberately. Source name and lifeline name are separate, and the source semantic parent is independent of the scenario. No allocation or capability involvement is inferred.",
|
||||
"Choose a message style in the tool selector. Start at a highlighted sequence position on a lifeline, then click or drag to the receiving lifeline. Complete the exchange and details in the form, which retains that sequence position. Escape cancels the gesture.",
|
||||
"Call + correlated reply creates both occurrences in one edit. Reply to existing call offers valid unanswered calls. A local timer has an expression and a lifeline, with no global exchange. Create and delete messages describe participant lifetime; Model checks also flag risks inside repeated loops.",
|
||||
"Choose Add action, Observe state, Reuse scenario or Add fragment and click the intended sequence position. Clicking an action or observation on a lifeline also selects that lifeline. Complete any remaining choices before Create.",
|
||||
"In Select / reorder, click an occurrence and drag its ↕ handle to another position within the same sequence. The ordered list's up/down controls provide another way to reorder. Select an occurrence to inspect its particular use of the exchange, including its message values.",
|
||||
],
|
||||
),
|
||||
walkthrough(
|
||||
"Describe alternatives",
|
||||
&[
|
||||
"Choose Fragment and select ALT, PAR, LOOP or OPT. Enter the branch conditions; the tool creates the fragment and its operands together.",
|
||||
"Use the Destination selector in subsequent message or action forms to choose the intended operand. New occurrences append to that operand's ordered behavior.",
|
||||
"Use Reuse scenario to refer to existing behavior. Bind every referenced lifeline to a compatible local lifeline explicitly. Open source follows the original scenario without copying its contents.",
|
||||
"Choose Add fragment, click its sequence position and select ALT, PAR, LOOP or OPT. Enter the branch conditions; the tool creates the fragment and its operands together.",
|
||||
"Click the highlighted position inside an operand to place the next message, action or reference there. The form shows its destination and insertion position. Append to this sequence instead explicitly moves the pending insertion to that sequence's end.",
|
||||
"Choose Reuse scenario and click its position, then bind every referenced lifeline to a compatible local lifeline explicitly. Select the resulting use and choose Open definition. Back to caller returns to that exact use and its viewport after you apply or cancel pending edits.",
|
||||
"Inspect the scenario projection and Model checks. State exactly what each guard means, including units and assumptions where relevant.",
|
||||
],
|
||||
),
|
||||
explain(
|
||||
"Read lifeline creation and deletion",
|
||||
"The Lane header and dotted guide identify a lifeline; the guide alone does not establish its lifetime. The stronger solid line shows where the lifeline is known to exist on this scenario path. Create starts that line at a named capsule; Delete ends it at an X. Select the capsule or X to edit that message occurrence. Select the header or guide to inspect the lifeline.",
|
||||
),
|
||||
explain(
|
||||
"Distinguish uncertain lifetime from an unanswered call",
|
||||
"A ? means lifetime depends on an alternative, optional, repeated or parallel path. Operands have independent lifetimes: Delete in one alternative does not delete a lifeline in another. Beyond such a fragment, the guide can continue without a definite solid line. This uncertainty is not by itself a model error. The … beside an activation marks a Call that remains unanswered on that path. A reused scenario's internal Create/Delete events appear when you choose Open definition; they do not create or delete the caller's lifelines.",
|
||||
),
|
||||
walkthrough(
|
||||
"Invoke an interface operation",
|
||||
&[
|
||||
@@ -543,7 +551,9 @@ fn article(topic: Topic) -> Article {
|
||||
walkthrough(
|
||||
"Build a state machine",
|
||||
&[
|
||||
"In Behavior & scenarios, create or select a State machine, then choose Create diagram. This places its existing regions and vertices in a saved view. + View creates another independent layout.",
|
||||
"In Behavior & scenarios → New → State machine, enter its name and search for the existing same-layer participant that owns it. Candidate labels show parent context and alias to distinguish repeated names.",
|
||||
"If the owner does not exist, Create new owner retains the machine name and first-view option. Choose its participant kind and semantic parent explicitly. Use this new source returns without changing the model; Create and open commits owner, machine and optional view together.",
|
||||
"Optionally select Create the first state view, then Create and open. The new machine starts without regions or states. For an existing machine, Create diagram places its existing regions and vertices in a saved view; + View creates another independent layout.",
|
||||
"Choose + Region and click the highlighted machine, or a State/Mode for a nested region. Choose Initial, State, Mode or another kind from + State / symbol, then click its highlighted owning region. Rename the new element in Properties.",
|
||||
"Choose Transition and drag from a source vertex to a compatible target, or click the source and then the target. In Select / move, drag states or containers to arrange them; a container moves its descendant placements without changing ownership.",
|
||||
"Select a drawn transition and drag its endpoint handle to reconnect it. Its right-click menu also offers Reconnect source and Reconnect target; choose one, then click a compatible vertex.",
|
||||
@@ -696,6 +706,10 @@ fn article(topic: Topic) -> Article {
|
||||
"Map references by meaning",
|
||||
"Reference mapping connect exact OA and SA identities used by the proposed change. Similar names are not sufficient. Map the records that play the same engineering role, including enclosing fields in qualified message-value paths. Mapping a referenced record does not authorize creating, moving or editing that record. If it needs a counterpart, establish that correspondence first.",
|
||||
),
|
||||
explain(
|
||||
"Edit stored observations deliberately",
|
||||
"In a trace's Properties, Edit stored history (advanced)… edits the observations retained by that trace. Choose the fact and enter its Operational and System observations explicitly. Not applicable, an observed absent value, and no stored fact have different meanings. Historical references may retain the exact UUID of a deleted record. Apply changes validates the complete history; Undo restores it. These history edits leave both counterpart records unchanged. Use Review counterpart changes… for Keep/Take decisions about their current engineering content.",
|
||||
),
|
||||
walkthrough(
|
||||
"Review one message value or lifeline binding",
|
||||
&[
|
||||
@@ -894,6 +908,10 @@ fn article(topic: Topic) -> Article {
|
||||
"A property draft is not yet the model",
|
||||
"Apply changes validates the candidate. When it is rejected, correct the retained draft or use Revert. The canvas continues to show accepted values. Navigation that needs to apply an invalid draft may remain on the current record so your work is not lost. You can read this guide without applying or discarding that draft.",
|
||||
),
|
||||
explain(
|
||||
"A new behavior is still a draft",
|
||||
"Inside a source subform, Back or the first Escape returns to the unchanged parent. Use this new source stages the prerequisite only. Canceling the parent discards it; choosing an existing reference excludes the staged source from creation. Recovery retains both forms and their reserved identities. An open dialog still receives Escape first. Behavior → New keeps the exact name, owner and viewpoint choices outside the model until Create and open succeeds. Cancel or Escape discards only this draft; if a dialog is open, the first Escape dismisses that dialog. New refuses other unfinished property, source or behavior drafts without applying them. Recovery restores the creation form in its original layer, including rejected raw text, for correction or cancellation.",
|
||||
),
|
||||
explain(
|
||||
"Aliases and references",
|
||||
"Stable source aliases must follow the language's identifier rules and avoid reserved words. Use a simple descriptive identifier such as assess_incident. Spaces belong in the displayed Name, not the alias. Reference pickers offer compatible model records; creating the required target first is often the missing step.",
|
||||
|
||||
@@ -1663,3 +1663,388 @@ fn adding_bend_between_close_ports_stays_outside_owner_clearance() {
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn child_work_preserves_allocated_context_and_atomic_history() {
|
||||
use oa_core::projection::project_view;
|
||||
for (layer, performer_kind, work_kind) in [
|
||||
(Layer::Oa, Kind::Entity, Kind::Activity),
|
||||
(Layer::Sa, Kind::System, Kind::Function),
|
||||
] {
|
||||
for folded in [false, true] {
|
||||
let mut p = Project::empty();
|
||||
let performer = p
|
||||
.add_element(performer_kind, layer, "Performer", None)
|
||||
.unwrap();
|
||||
let work = p
|
||||
.add_element(work_kind, layer, "Parent work", None)
|
||||
.unwrap();
|
||||
let allocation = p.relate(
|
||||
RelationKind::Allocation,
|
||||
"Engineering assignment",
|
||||
performer,
|
||||
work,
|
||||
);
|
||||
p.relations.get_mut(&allocation).unwrap().description =
|
||||
"Retain assignment metadata".into();
|
||||
let original = p.relations[&allocation].clone();
|
||||
let diagram = p.ensure_diagram(layer);
|
||||
p.diagrams.get_mut(&diagram).unwrap().projection = ProjectionKind::Allocation;
|
||||
let performer_node = p.place(diagram, performer, Position { x: 60., y: 60. });
|
||||
let work_node = p.place(diagram, work, Position { x: 84., y: 124. });
|
||||
p = execute_batch(&p, &[Command::Layout(diagram)]).unwrap();
|
||||
for node in [performer_node, work_node] {
|
||||
p.diagrams
|
||||
.get_mut(&diagram)
|
||||
.unwrap()
|
||||
.nodes
|
||||
.get_mut(&node)
|
||||
.unwrap()
|
||||
.collapsed = folded;
|
||||
}
|
||||
let mut app = App::with_project(p);
|
||||
send(&mut app, Message::Layer(layer));
|
||||
send(&mut app, Message::Page(Page::Diagram));
|
||||
send(
|
||||
&mut app,
|
||||
Message::Diagram(crate::app::Choice {
|
||||
id: diagram,
|
||||
label: String::new(),
|
||||
}),
|
||||
);
|
||||
let mut first = None;
|
||||
for step in 0..3 {
|
||||
let parent = if step == 2 { first.unwrap() } else { work };
|
||||
send(&mut app, Message::Select(parent));
|
||||
let before = app.project().clone();
|
||||
let before_source = app.document.bundle().clone();
|
||||
send(&mut app, Message::CreateContextElement(work_kind, true));
|
||||
let created = app.selected;
|
||||
assert!(
|
||||
!before.elements.contains_key(&created),
|
||||
"{layer:?} step {step}: {}",
|
||||
app.status
|
||||
);
|
||||
assert_eq!(app.project().elements.len(), before.elements.len() + 1);
|
||||
assert_eq!(app.project().elements[&created].parent, Some(parent));
|
||||
for (id, element) in &before.elements {
|
||||
assert_eq!(&app.project().elements[id], element);
|
||||
}
|
||||
assert!(
|
||||
!oa_core::validation::validate(app.project())
|
||||
.iter()
|
||||
.any(|i| i.severity == oa_core::validation::Severity::Error)
|
||||
);
|
||||
assert_eq!(
|
||||
oa_core::validation::allocation_summary(app.project(), work),
|
||||
Some(performer)
|
||||
);
|
||||
assert_eq!(
|
||||
oa_core::validation::effective_performer(app.project(), created),
|
||||
Some(performer)
|
||||
);
|
||||
assert!(
|
||||
!app.project()
|
||||
.relations
|
||||
.values()
|
||||
.any(|r| r.kind() == RelationKind::Allocation && r.target() == parent)
|
||||
);
|
||||
if step != 1 {
|
||||
let mut expected = original.clone();
|
||||
if let RelationBody::Allocation { target, .. } = &mut expected.body {
|
||||
*target = created;
|
||||
}
|
||||
assert_eq!(app.project().relations[&allocation], expected);
|
||||
} else {
|
||||
assert_eq!(
|
||||
app.project().relations[&allocation],
|
||||
before.relations[&allocation]
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
app.project().relations.len(),
|
||||
before.relations.len() + usize::from(step == 1)
|
||||
);
|
||||
let view = project_view(app.project(), diagram);
|
||||
let parent_node = view.nodes.iter().find(|n| n.element == parent).unwrap();
|
||||
let child_node = view.nodes.iter().find(|n| n.element == created).unwrap();
|
||||
let root_node = view.nodes.iter().find(|n| n.element == work).unwrap();
|
||||
assert_eq!(root_node.visual_parent, Some(performer_node));
|
||||
assert_eq!(child_node.visual_parent, Some(parent_node.id));
|
||||
assert!(!app.project().diagrams[&diagram].nodes[&performer_node].collapsed);
|
||||
assert!(!app.project().diagrams[&diagram].nodes[&parent_node.id].collapsed);
|
||||
assert!(child_node.position.x >= parent_node.position.x);
|
||||
assert!(child_node.position.y >= parent_node.position.y + 64.);
|
||||
assert!(
|
||||
child_node.position.x + child_node.size.width
|
||||
<= parent_node.position.x + parent_node.size.width
|
||||
);
|
||||
assert!(
|
||||
child_node.position.y + child_node.size.height
|
||||
<= parent_node.position.y + parent_node.size.height
|
||||
);
|
||||
for sibling in view
|
||||
.nodes
|
||||
.iter()
|
||||
.filter(|n| n.visual_parent == Some(parent_node.id) && n.id != child_node.id)
|
||||
{
|
||||
assert!(sibling.position.y + sibling.size.height <= child_node.position.y);
|
||||
}
|
||||
let after = app.project().clone();
|
||||
let after_source = app.document.bundle().clone();
|
||||
send(&mut app, Message::Undo);
|
||||
assert_eq!(
|
||||
app.project(),
|
||||
&before,
|
||||
"creation, allocation and reveal/reflow undo together"
|
||||
);
|
||||
assert_eq!(app.document.bundle(), &before_source);
|
||||
send(&mut app, Message::Redo);
|
||||
assert_eq!(app.project(), &after);
|
||||
assert_eq!(app.document.bundle(), &after_source);
|
||||
if step == 0 {
|
||||
first = Some(created);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn child_work_does_not_invent_unallocated_or_mixed_performers() {
|
||||
for (layer, performer_kind, other_kind, work_kind) in [
|
||||
(Layer::Oa, Kind::Entity, Kind::Entity, Kind::Activity),
|
||||
(Layer::Sa, Kind::System, Kind::SystemActor, Kind::Function),
|
||||
] {
|
||||
// Root leaf, partly unallocated composite, and mixed-performer composite.
|
||||
for context in 0..3 {
|
||||
let mut p = Project::empty();
|
||||
let performer = p
|
||||
.add_element(performer_kind, layer, "Performer", None)
|
||||
.unwrap();
|
||||
let other = p
|
||||
.add_element(other_kind, layer, "Other performer", None)
|
||||
.unwrap();
|
||||
let work = p
|
||||
.add_element(work_kind, layer, "Parent work", None)
|
||||
.unwrap();
|
||||
let mut placed = vec![performer, other, work];
|
||||
if context != 0 {
|
||||
let first = p
|
||||
.add_element(work_kind, layer, "First leaf", Some(work))
|
||||
.unwrap();
|
||||
let second = p
|
||||
.add_element(work_kind, layer, "Second leaf", Some(work))
|
||||
.unwrap();
|
||||
p.relate(
|
||||
RelationKind::Allocation,
|
||||
"First assignment",
|
||||
performer,
|
||||
first,
|
||||
);
|
||||
if context == 2 {
|
||||
p.relate(RelationKind::Allocation, "Other assignment", other, second);
|
||||
}
|
||||
placed.extend([first, second]);
|
||||
}
|
||||
assert_eq!(oa_core::validation::allocation_summary(&p, work), None);
|
||||
let diagram = p.ensure_diagram(layer);
|
||||
p.diagrams.get_mut(&diagram).unwrap().projection = ProjectionKind::Allocation;
|
||||
for id in placed {
|
||||
p.place(diagram, id, Position { x: 60., y: 60. });
|
||||
}
|
||||
p = execute_batch(&p, &[Command::Layout(diagram)]).unwrap();
|
||||
let mut app = App::with_project(p);
|
||||
send(&mut app, Message::Layer(layer));
|
||||
send(&mut app, Message::Page(Page::Diagram));
|
||||
send(
|
||||
&mut app,
|
||||
Message::Diagram(crate::app::Choice {
|
||||
id: diagram,
|
||||
label: String::new(),
|
||||
}),
|
||||
);
|
||||
send(&mut app, Message::Select(work));
|
||||
let before = app.project().clone();
|
||||
let before_source = app.document.bundle().clone();
|
||||
send(&mut app, Message::CreateContextElement(work_kind, true));
|
||||
let child = app.selected;
|
||||
assert!(!before.elements.contains_key(&child), "{}", app.status);
|
||||
assert_eq!(app.project().elements[&child].parent, Some(work));
|
||||
assert_eq!(app.project().relations, before.relations);
|
||||
assert_eq!(
|
||||
oa_core::validation::effective_performer(app.project(), child),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
oa_core::validation::allocation_summary(app.project(), work),
|
||||
None
|
||||
);
|
||||
let after = app.project().clone();
|
||||
let after_source = app.document.bundle().clone();
|
||||
send(&mut app, Message::Undo);
|
||||
assert_eq!(app.project(), &before);
|
||||
assert_eq!(app.document.bundle(), &before_source);
|
||||
send(&mut app, Message::Redo);
|
||||
assert_eq!(app.project(), &after);
|
||||
assert_eq!(app.document.bundle(), &after_source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn child_work_transfers_leaf_roles_without_fabricating_responsibilities() {
|
||||
// Direct allocation + matching roles, uniquely realized roles only,
|
||||
// unresolved roles, and roles realized by different performers.
|
||||
for context in 0..4 {
|
||||
let mut p = Project::empty();
|
||||
let performer = p
|
||||
.add_element(Kind::Entity, Layer::Oa, "Performer", None)
|
||||
.unwrap();
|
||||
let other = p
|
||||
.add_element(Kind::Entity, Layer::Oa, "Other performer", None)
|
||||
.unwrap();
|
||||
let work = p
|
||||
.add_element(Kind::Activity, Layer::Oa, "Responsible work", None)
|
||||
.unwrap();
|
||||
let mut responsibilities = Vec::new();
|
||||
if context == 0 {
|
||||
responsibilities.push(p.relate(
|
||||
RelationKind::Allocation,
|
||||
"Direct responsibility",
|
||||
performer,
|
||||
work,
|
||||
));
|
||||
}
|
||||
for index in 0..2 {
|
||||
let role = p
|
||||
.add_element(Kind::Role, Layer::Oa, &format!("Role {index}"), None)
|
||||
.unwrap();
|
||||
responsibilities.push(p.relate(
|
||||
RelationKind::RoleAssignment,
|
||||
&format!("Assigned role {index}"),
|
||||
role,
|
||||
work,
|
||||
));
|
||||
if context != 2 {
|
||||
let source = if context == 3 && index == 1 {
|
||||
other
|
||||
} else {
|
||||
performer
|
||||
};
|
||||
p.relate(RelationKind::RoleRealization, "Realizes role", source, role);
|
||||
}
|
||||
}
|
||||
for (index, id) in responsibilities.iter().enumerate() {
|
||||
let relation = p.relations.get_mut(id).unwrap();
|
||||
relation.description =
|
||||
format!("Responsibility {index}: preserve engineering rationale");
|
||||
let link = Id::new_v4();
|
||||
p.artifact_links.insert(
|
||||
link,
|
||||
ArtifactLink {
|
||||
id: link,
|
||||
alias: format!("responsibility_evidence_{index}"),
|
||||
name: format!("Responsibility evidence {index}"),
|
||||
description: "Preserve the reference to this exact relation".into(),
|
||||
owner: *id,
|
||||
relation: ArtifactRelation::References,
|
||||
uri: format!("https://example.invalid/responsibility/{index}"),
|
||||
revision: Some("review-7".into()),
|
||||
fragment: Some("assignment".into()),
|
||||
},
|
||||
);
|
||||
}
|
||||
let original_relations = p.relations.clone();
|
||||
let diagram = p.ensure_diagram(Layer::Oa);
|
||||
p.diagrams.get_mut(&diagram).unwrap().projection = ProjectionKind::Allocation;
|
||||
p.place(diagram, performer, Position { x: 60., y: 60. });
|
||||
p.place(diagram, work, Position { x: 84., y: 124. });
|
||||
p = execute_batch(&p, &[Command::Layout(diagram)]).unwrap();
|
||||
let expected_performer = (context < 2).then_some(performer);
|
||||
assert_eq!(
|
||||
oa_core::validation::allocation_summary(&p, work),
|
||||
expected_performer
|
||||
);
|
||||
let mut app = App::with_project(p);
|
||||
send(&mut app, Message::Page(Page::Diagram));
|
||||
send(
|
||||
&mut app,
|
||||
Message::Diagram(crate::app::Choice {
|
||||
id: diagram,
|
||||
label: String::new(),
|
||||
}),
|
||||
);
|
||||
let mut first = None;
|
||||
for step in 0..2 {
|
||||
send(&mut app, Message::Select(work));
|
||||
let before = app.project().clone();
|
||||
let before_source = app.document.bundle().clone();
|
||||
send(
|
||||
&mut app,
|
||||
Message::CreateContextElement(Kind::Activity, true),
|
||||
);
|
||||
let child = app.selected;
|
||||
assert!(
|
||||
!before.elements.contains_key(&child),
|
||||
"context {context}, step {step}: {}",
|
||||
app.status
|
||||
);
|
||||
assert_eq!(app.project().elements[&child].parent, Some(work));
|
||||
if step == 0 {
|
||||
first = Some(child);
|
||||
}
|
||||
for (id, original) in &original_relations {
|
||||
let mut expected = original.clone();
|
||||
if responsibilities.contains(id) {
|
||||
match &mut expected.body {
|
||||
RelationBody::Allocation { target, .. }
|
||||
| RelationBody::RoleAssignment { target, .. } => *target = first.unwrap(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
assert_eq!(app.project().relations[id], expected);
|
||||
}
|
||||
assert_eq!(app.project().artifact_links, before.artifact_links);
|
||||
assert!(!app.project().artifact_links.is_empty());
|
||||
let added = usize::from(step == 1 && expected_performer.is_some());
|
||||
assert_eq!(
|
||||
app.project().relations.len(),
|
||||
before.relations.len() + added
|
||||
);
|
||||
assert_eq!(
|
||||
oa_core::validation::effective_performer(app.project(), child),
|
||||
expected_performer
|
||||
);
|
||||
assert_eq!(
|
||||
app.project()
|
||||
.relations
|
||||
.values()
|
||||
.filter(|r| r.kind() == RelationKind::RoleAssignment && r.target() == child)
|
||||
.count(),
|
||||
if step == 0 { 2 } else { 0 }
|
||||
);
|
||||
if context != 0 && step == 0 {
|
||||
assert!(
|
||||
!app.project()
|
||||
.relations
|
||||
.values()
|
||||
.any(|r| r.kind() == RelationKind::Allocation)
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
!oa_core::validation::validate(app.project())
|
||||
.iter()
|
||||
.any(|i| i.severity == oa_core::validation::Severity::Error)
|
||||
);
|
||||
let after = app.project().clone();
|
||||
let after_source = app.document.bundle().clone();
|
||||
send(&mut app, Message::Undo);
|
||||
assert_eq!(app.project(), &before);
|
||||
assert_eq!(app.document.bundle(), &before_source);
|
||||
send(&mut app, Message::Redo);
|
||||
assert_eq!(app.project(), &after);
|
||||
assert_eq!(app.document.bundle(), &after_source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,13 +228,15 @@ pub fn view(app: &App) -> View<'_, Message> {
|
||||
Page::Guide => crate::guide::view(&app.guide),
|
||||
};
|
||||
let process_layout = app.topology_context().is_some();
|
||||
let focus_process = process_layout && app.behavior.topology.focused;
|
||||
let scenario_layout = app.scenario_context().is_some();
|
||||
let focus_behavior = (process_layout && app.behavior.topology.focused)
|
||||
|| (scenario_layout && app.behavior.scenario.focused());
|
||||
let mut body = if app.page == Page::Home {
|
||||
row![container(main).width(Fill).height(Fill)].height(Fill)
|
||||
} else if process_layout {
|
||||
} else if process_layout || scenario_layout {
|
||||
// Keep main at the same widget-tree path across Focus toggles. The
|
||||
// collapsed slot has no controls, so it cannot paint over or hit the canvas.
|
||||
let process_sidebar: View<'_, Message> = if focus_process {
|
||||
let process_sidebar: View<'_, Message> = if focus_behavior {
|
||||
Space::new().width(0).into()
|
||||
} else {
|
||||
sidebar.into()
|
||||
@@ -242,7 +244,7 @@ pub fn view(app: &App) -> View<'_, Message> {
|
||||
// Row::push would discard the zero-width slot and shift main again.
|
||||
widget::Row::from_vec(vec![
|
||||
container(process_sidebar)
|
||||
.width(if focus_process { 0 } else { 260 })
|
||||
.width(if focus_behavior { 0 } else { 260 })
|
||||
.height(Fill)
|
||||
.clip(true)
|
||||
.into(),
|
||||
@@ -253,7 +255,7 @@ pub fn view(app: &App) -> View<'_, Message> {
|
||||
} else {
|
||||
row![sidebar, container(main).width(Fill).height(Fill)].height(Fill)
|
||||
};
|
||||
if !focus_process && !matches!(app.page, Page::Home | Page::Text | Page::Guide) {
|
||||
if !focus_behavior && !matches!(app.page, Page::Home | Page::Text | Page::Guide) {
|
||||
body = body.push(inspector(app));
|
||||
}
|
||||
let footer = container(
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
Run from any directory with Python 3: semantic_schema.py [--check] [--validate].
|
||||
--validate additionally runs source-7 structural fixtures with jsonschema.
|
||||
This generates documentation only, never runtime selectors or model mutations.
|
||||
The desktop stored-history editor reads the checked-in descriptor for field
|
||||
shapes. Core types and validation remain authoritative for model mutations.
|
||||
"""
|
||||
import copy
|
||||
import json
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
# Requires --build-context server_notices=<prepared server-notices directory>.
|
||||
# See docs/linux-packaging.md. Missing/stale notices fail before application build.
|
||||
FROM rust:1.90-bookworm AS builder
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends python3 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
WORKDIR /src
|
||||
COPY Cargo.toml Cargo.lock ./
|
||||
COPY LICENSE ./LICENSE
|
||||
COPY build ./build
|
||||
COPY crates ./crates
|
||||
COPY docs/openapi.json ./docs/openapi.json
|
||||
COPY scripts/package-artifacts.py ./scripts/package-artifacts.py
|
||||
COPY --from=server_notices / /tmp/server-notices/
|
||||
RUN python3 scripts/package-artifacts.py verify-notices \
|
||||
--directory /tmp/server-notices --repository /src --target x86_64-unknown-linux-gnu
|
||||
RUN cargo build --release --locked -p oa-server
|
||||
|
||||
FROM debian:bookworm-slim
|
||||
@@ -12,6 +22,7 @@ RUN groupadd --gid 10001 arcadia \
|
||||
&& mkdir /data \
|
||||
&& chown 10001:10001 /data
|
||||
COPY --from=builder /src/target/release/arcadia-server /usr/local/bin/arcadia-server
|
||||
COPY --from=builder /tmp/server-notices/ /usr/share/doc/arcadia-server/
|
||||
USER 10001:10001
|
||||
VOLUME ["/data"]
|
||||
EXPOSE 8787
|
||||
|
||||
@@ -1,9 +1,24 @@
|
||||
.git
|
||||
target
|
||||
dist
|
||||
harness
|
||||
# Only inputs consumed by this Dockerfile enter the ordinary repository context.
|
||||
# The server_notices named context is supplied separately and verified in builder.
|
||||
**
|
||||
!Cargo.toml
|
||||
!Cargo.lock
|
||||
!LICENSE
|
||||
!build/
|
||||
!build/**
|
||||
!crates/
|
||||
!crates/**
|
||||
!docs/
|
||||
!docs/openapi.json
|
||||
!scripts/
|
||||
!scripts/package-artifacts.py
|
||||
!deployment/
|
||||
!deployment/Dockerfile
|
||||
!deployment/Dockerfile.dockerignore
|
||||
# Do not admit generated/private files even inside an allowed source tree.
|
||||
**/target
|
||||
**/*.sqlite3
|
||||
**/.git
|
||||
**/.env
|
||||
**/.env.*
|
||||
**/*.log
|
||||
.codex
|
||||
.openai
|
||||
**/*.sqlite3
|
||||
|
||||
+120
-25
@@ -752,7 +752,7 @@ lists all additional fields. <span class="docutils literal">ref</span> means <sp
|
||||
</tr>
|
||||
<tr><td><p><span class="docutils literal">oa_scenario</span>, <span class="docutils literal">sa_scenario</span></p></td>
|
||||
<td><p><span class="docutils literal">viewpoint</span>: ScenarioViewpoint; <span class="docutils literal">context</span>: ref?; <span class="docutils literal">precondition</span>, <span class="docutils literal">postcondition</span>: strings; <span class="docutils literal">steps</span>: occurrence refs</p></td>
|
||||
<td><p>MixedLegacy for omitted schema-2 source; none; <span class="docutils literal">""</span>; <span class="docutils literal">""</span>; <span class="docutils literal">[]</span></p></td>
|
||||
<td><p>MixedLegacy when viewpoint is omitted, including newly authored source 7 (see section 4.3); none; <span class="docutils literal">""</span>; <span class="docutils literal">""</span>; <span class="docutils literal">[]</span></p></td>
|
||||
</tr>
|
||||
<tr><td><p><span class="docutils literal">oa_lifeline</span>, <span class="docutils literal">sa_lifeline</span></p></td>
|
||||
<td><p><span class="docutils literal">represented</span>: participant, work, or role ref?</p></td>
|
||||
@@ -792,49 +792,71 @@ An Enumeration contains unique string literals. For example:</p>
|
||||
<pre class="code text literal-block"><code>definition { Enumeration { literals ["Clear", "Fog"]; }; };
|
||||
type_ref { Named @weather_classification; };</code></pre>
|
||||
<p>These are typed constructors with named fields, not embedded JSON documents.
|
||||
Constructor field names and enum spellings are case-sensitive. When replacing
|
||||
a constructor, all of its fields MUST be supplied; the containing declaration
|
||||
defaults do not silently fill a different constructor's missing fields.
|
||||
During schema-2 compatibility, the new Exchange fields <span class="docutils literal">message_kind</span>,
|
||||
<span class="docutils literal">reply_to</span> and <span class="docutils literal">values</span> are exceptions: omitted values hydrate as
|
||||
Asynchronous, none and [] respectively. Canonical output writes them explicitly.
|
||||
This preserves older schema-2 source/JSON without inventing call correlation or
|
||||
value annotations. It is not a rule for an unspecified future schema.</p>
|
||||
Constructor field names and enum spellings are case-sensitive. A selected
|
||||
constructor uses its own field contract; it does not inherit the payload of a
|
||||
different constructor from the declaration default. Required fields MUST be
|
||||
supplied. Nullability and omission are separate properties: <span class="docutils literal">?</span> denotes a
|
||||
value that can be <span class="docutils literal">none</span>; a constructor's omission contract determines whether
|
||||
the field may be absent. There is no general all-constructor-fields-required
|
||||
rule. Other typed constructors use their contracts in the corresponding
|
||||
sections; the occurrence rules below do not establish defaults for them.</p>
|
||||
<p>The following table records the source-7 OccurrenceBody contracts. Optional
|
||||
references may be omitted and then mean <span class="docutils literal">none</span>. Exchange also admits omitted
|
||||
<span class="docutils literal">message_kind</span> and <span class="docutils literal">values</span>, meaning Asynchronous and <span class="docutils literal">[]</span> respectively.
|
||||
These omissions are accepted in newly authored source 7, not only in schema-2
|
||||
compatibility input. Earlier schema admission of constructors and fields is
|
||||
still governed by its version rules.</p>
|
||||
<p>Canonical formatting writes the hydrated occurrence references and default
|
||||
<span class="docutils literal">message_kind</span>, <span class="docutils literal">reply_to</span> and <span class="docutils literal">values</span> explicitly. An absent
|
||||
<span class="docutils literal">operation</span> remains omitted in canonical output. Lossless no-op updates and
|
||||
unrelated name edits retain authored omissions; canonical formatting is a
|
||||
different operation. An admitted incomplete occurrence can still produce model
|
||||
checks requesting its missing work, exchange, lifeline or referenced scenario.</p>
|
||||
<table>
|
||||
<caption>Occurrence body constructors</caption>
|
||||
<colgroup>
|
||||
<col style="width: 22.0%" />
|
||||
<col style="width: 78.0%" />
|
||||
<col style="width: 18.0%" />
|
||||
<col style="width: 36.0%" />
|
||||
<col style="width: 46.0%" />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr><th class="head"><p>Constructor</p></th>
|
||||
<th class="head"><p>Required fields</p></th>
|
||||
<th class="head"><p>Fields that may be omitted, with their resulting values</p></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td><p>Exchange</p></td>
|
||||
<td><p><span class="docutils literal">exchange</span>: exchange relation ref?; <span class="docutils literal">sender</span>, <span class="docutils literal">receiver</span>: lifeline refs?; <span class="docutils literal">message_kind</span>: MessageKind; <span class="docutils literal">reply_to</span>: occurrence ref?; <span class="docutils literal">values</span>: MessageValue records</p></td>
|
||||
<td><p>None</p></td>
|
||||
<td><p><span class="docutils literal">exchange</span>: exchange relation ref?, <span class="docutils literal">sender</span>, <span class="docutils literal">receiver</span>: lifeline refs?, <span class="docutils literal">reply_to</span>: occurrence ref?, <span class="docutils literal">operation</span>: OperationUse? → <span class="docutils literal">none</span>; <span class="docutils literal">message_kind</span>: MessageKind → Asynchronous; <span class="docutils literal">values</span>: MessageValue records → <span class="docutils literal">[]</span></p></td>
|
||||
</tr>
|
||||
<tr><td><p>Action</p></td>
|
||||
<td><p><span class="docutils literal">action</span>: activity/function ref?; <span class="docutils literal">lifeline</span>: ref?</p></td>
|
||||
<td><p>None</p></td>
|
||||
<td><p><span class="docutils literal">action</span>: activity/function ref?, <span class="docutils literal">lifeline</span>: ref? → <span class="docutils literal">none</span></p></td>
|
||||
</tr>
|
||||
<tr><td><p>Control</p></td>
|
||||
<td><p><span class="docutils literal">kind</span>: Choice, Merge, Fork, Join, Loop</p></td>
|
||||
<td><p>None</p></td>
|
||||
</tr>
|
||||
<tr><td><p>SequenceReference</p></td>
|
||||
<td><p><span class="docutils literal">sequence</span>: process/functional-chain ref?</p></td>
|
||||
<td><p>None</p></td>
|
||||
<td><p><span class="docutils literal">sequence</span>: process/functional-chain ref? → <span class="docutils literal">none</span></p></td>
|
||||
</tr>
|
||||
<tr><td><p>ScenarioUse</p></td>
|
||||
<td><p><span class="docutils literal">scenario</span>: scenario ref?; <span class="docutils literal">bindings</span>: LifelineBinding records</p></td>
|
||||
<td><p><span class="docutils literal">bindings</span>: LifelineBinding records</p></td>
|
||||
<td><p><span class="docutils literal">scenario</span>: scenario ref? → <span class="docutils literal">none</span></p></td>
|
||||
</tr>
|
||||
<tr><td><p>State</p></td>
|
||||
<td><p><span class="docutils literal">state</span>: state/mode ref?; <span class="docutils literal">lifeline</span>: ref?</p></td>
|
||||
<td><p>None</p></td>
|
||||
<td><p><span class="docutils literal">state</span>: state/mode ref?, <span class="docutils literal">lifeline</span>: ref? → <span class="docutils literal">none</span></p></td>
|
||||
</tr>
|
||||
<tr><td><p>Fragment</p></td>
|
||||
<td><p><span class="docutils literal">operator</span>: Alternative, Parallel, Loop, Optional; <span class="docutils literal">branches</span>: branch occurrence refs</p></td>
|
||||
<td><p>None</p></td>
|
||||
</tr>
|
||||
<tr><td><p>Branch</p></td>
|
||||
<td><p><span class="docutils literal">guard</span>: string; <span class="docutils literal">occurrences</span>: occurrence refs</p></td>
|
||||
<td><p>None</p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -1274,9 +1296,21 @@ reference checks.</p>
|
||||
<p>Omitted schema-2 viewpoints hydrate as MixedLegacy in both source and JSON.
|
||||
This preserves pre-extension mixed work/participant scenes without discarding
|
||||
their lifelines or silently assigning a stricter meaning. MixedLegacy emits a
|
||||
review warning. New graphical factories explicitly select OperationalActivity
|
||||
or Functional by layer. Authors can select a strict viewpoint after reviewing
|
||||
the legacy scene; canonical source then records that choice.</p>
|
||||
review warning. Source parsing also uses MixedLegacy when <span class="docutils literal">viewpoint</span> is
|
||||
omitted from a newly authored source-7 scenario; it does not infer a strict
|
||||
viewpoint from the layer or lifelines. Write the intended viewpoint explicitly
|
||||
when starting a scenario. For example, this new OA source project starts an
|
||||
activity-based scenario whose lifelines and occurrences can be added later:</p>
|
||||
<pre class="literal-block">arcadia 7;
|
||||
project response "Distress response" {
|
||||
oa_scenario assessment "Assess the distress report" {
|
||||
viewpoint OperationalActivity;
|
||||
}
|
||||
}</pre>
|
||||
<p>An empty scenario can produce completeness warnings while remaining editable.
|
||||
The desktop's Behavior <strong>New</strong> flow asks authors to choose one of the layer's
|
||||
two strict viewpoints explicitly. Authors can also select a strict viewpoint
|
||||
after reviewing a legacy scene; canonical source then records that choice.</p>
|
||||
<p>MessageKind is Asynchronous, Call, Reply, Create, Delete, or
|
||||
<span class="docutils literal">{ Timer { expression "after 5 s"; }; }</span>. A process/chain exchange
|
||||
contribution MUST remain Asynchronous with no reply correlation. Call/reply,
|
||||
@@ -1285,16 +1319,25 @@ references a Call occurrence in the same scenario. The call MUST precede the
|
||||
reply on a guaranteed common sequence/enclosing branch path. Reply lifelines
|
||||
reverse the call's sender/receiver; it may reuse the call's exchange in reverse
|
||||
or use an explicit reverse-direction exchange. A second reply to the same call
|
||||
on that guaranteed path is an error. Missing reply correlation is a draft
|
||||
that can follow another reply on a shared execution path, including conditional
|
||||
or parallel operands, is an error. Missing reply correlation is a draft
|
||||
warning; non-Reply messages MUST NOT have <span class="docutils literal">reply_to</span>.</p>
|
||||
<p>Create identifies the receiver lifeline's creation. An earlier guaranteed use
|
||||
of that lifeline is an error. Delete identifies its destruction; a later
|
||||
guaranteed use is an error. Ordering checks respect branch ownership; they MUST
|
||||
NOT flatten alternative branches into a false unconditional order. A Timer is
|
||||
<p>Create identifies the receiver lifeline's creation. Another use that can precede
|
||||
its creation on a shared execution path is an error. Delete identifies its
|
||||
destruction; another use that can follow it on a shared execution path is an
|
||||
error. These checks include conditional and parallel operands but exclude
|
||||
mutually exclusive alternatives. Ordering checks respect branch ownership; they
|
||||
MUST NOT flatten alternative branches into a false unconditional order. A Timer is
|
||||
local to its sender lifeline, has no global exchange or values, and has either
|
||||
no receiver or the same receiver as sender. Its nonempty expression describes
|
||||
the delay/deadline; an empty expression warns. Conditions and expressions are
|
||||
stored engineering annotations, not an executable simulation language.</p>
|
||||
<p>These ordering checks compare distinct stored occurrences along structural
|
||||
paths; they do not simulate repeated loop iterations. Repetition risks use
|
||||
<span class="docutils literal">reply_repetition</span> and <span class="docutils literal">lifeline_repetition</span> warnings separately. For example,
|
||||
a loop containing one Reply to an outer Call can warn about repetition without
|
||||
containing two distinct replies; a same-operand Create/use/Delete lifetime can
|
||||
repeat without its previous iteration being treated as an earlier stored use.</p>
|
||||
<p>Each MessageValue is <span class="docutils literal">{ path [@parameter]; field @data_field; expression "value expression"; }</span>.
|
||||
Its identity is the pair (path, field), so two uses of one Record can have
|
||||
different values for the same terminal field. Path contains enclosing DataField
|
||||
@@ -2217,6 +2260,23 @@ strings use JSON string escaping, including <span class="docutils literal">\n</s
|
||||
and Unicode escapes. Literal unescaped newlines inside strings are invalid.
|
||||
Numbers use JSON decimal/exponent syntax; NaN, infinity, hexadecimal numbers,
|
||||
leading plus signs, and leading zeroes are invalid.</p>
|
||||
<p>A small model may begin with engineering declarations alone. This complete
|
||||
source describes two participants, their work, responsibility and an exchange:</p>
|
||||
<pre class="literal-block">arcadia 7;
|
||||
project distress "Distress response" {
|
||||
oa_actor vessel "Vessel in distress" {}
|
||||
oa_entity rescue "Rescue coordination" {}
|
||||
oa_activity report "Report distress" {}
|
||||
oa_activity assess "Assess incident" {}
|
||||
allocation vessel_reports "Crew reports" { source @vessel; target @report; }
|
||||
allocation rescue_assesses "Team assesses" { source @rescue; target @assess; }
|
||||
interaction distress_report "Distress report" { source @report; target @assess; }
|
||||
}</pre>
|
||||
<p>No diagram, placement, empty description or explicit root parent is required.
|
||||
The accepted source adds identities; keep those clauses during later edits.
|
||||
Changing <span class="docutils literal">"Assess incident"</span> changes the display name while <span class="docutils literal">@assess</span> still
|
||||
resolves to the same activity. Behavior-coverage warnings permit this intermediate
|
||||
model to be saved. Allocation remains explicit and distinct from decomposition.</p>
|
||||
<p>Aliases match <span class="docutils literal"><span class="pre">[A-Za-z_][A-Za-z0-9_]*</span></span>. The following aliases are reserved:
|
||||
arcadia, project, id, oa, sa, element, relation, diagram, node, artifact,
|
||||
trace, body, alias, name, description, parent, from, to, entity, actor,
|
||||
@@ -2227,7 +2287,7 @@ spellings can be used in alias positions because references begin with <span cla
|
||||
<span class="docutils literal"><span class="pre">element-kind</span></span> and <span class="docutils literal"><span class="pre">relation-kind</span></span> are exactly the corresponding declaration
|
||||
keywords listed above. Typed field
|
||||
sets and enum constructors are closed by sections 3–5.</p>
|
||||
<pre class="code text literal-block"><code>document = "arcadia" "5" ";" project
|
||||
<pre class="code text literal-block"><code>document = "arcadia" "7" ";" project
|
||||
project = "project" alias string identity? "{" project-entry* "}"
|
||||
project-entry = "description" string ";" | declaration
|
||||
declaration = element
|
||||
@@ -2589,6 +2649,41 @@ leaving dangling references. Deleting a sequence does not delete the semantic
|
||||
exchanges it references. Cross-layer counterparts are not owned descendants.
|
||||
Text edits describe a complete candidate and must remove or repair dependencies
|
||||
explicitly before Apply succeeds.</p>
|
||||
<section id="scenario-lifetime-presentation">
|
||||
<h3>8.1. Scenario lifetime presentation</h3>
|
||||
<p>Scenario lifetime geometry is derived from the stored occurrences and their
|
||||
sequence/operand ownership. It adds no model records, source annotations or
|
||||
history entries. A lifeline's identity header and neutral editing guide remain
|
||||
available before creation, after deletion and when its represented element is
|
||||
unfinished. The guide MUST be visually distinguishable from a live interval;
|
||||
it is an editing axis, not a statement that the participant exists.</p>
|
||||
<p>A directly ordered Create starts the receiver's live interval at its occurrence;
|
||||
Delete ends that receiver's interval at its occurrence. Create uses a named birth
|
||||
marker and Delete an X, each selectable through the original occurrence identity.
|
||||
The message's receiving endpoint MUST align with its marker, including self
|
||||
messages. A pre-existing lifeline without a creation has a live interval from
|
||||
below its header; without deletion it extends to the visible scenario bottom,
|
||||
including in an empty scenario. A missing lifecycle receiver MUST NOT be inferred from
|
||||
the exchange. An explicit local receiver remains meaningful when its exchange
|
||||
reference is unfinished.</p>
|
||||
<p>Operands MUST be projected independently from their enclosing entry context.
|
||||
A lifetime change in an alternative, optional, loop or parallel operand MUST
|
||||
NOT imply the same change in its displayed sibling or a definite lifetime
|
||||
after the fragment. When local structure does not establish one lifetime across
|
||||
the represented paths, the continuation is visually unresolved. A subsequent
|
||||
explicit Create or Delete in the enclosing sequence can establish a definite interval.
|
||||
Neither guard text, screen order nor a guessed loop count proves execution.
|
||||
ScenarioUse remains opaque: opening its definition may show its lifecycle,
|
||||
but a callee's geometry MUST NOT create or destroy a caller lifeline.</p>
|
||||
<p>Call activation is distinct from lifetime. A branch-local Delete clips that
|
||||
branch's visible activation at the death marker without inventing a Reply,
|
||||
removing an unresolved-call warning or terminating sibling paths.
|
||||
Solid activation bars are restricted to definite live intervals; an unresolved
|
||||
continuation retains its uncertainty/call diagnostic rather than depicting
|
||||
definite survival after a possible deletion. Selection,
|
||||
insertion slots, source edits and Undo/Redo retain the same semantic identities
|
||||
and validation boundary regardless of the rendered intervals.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="persistence-and-explicit-migration">
|
||||
<h2>9. Persistence and explicit migration</h2>
|
||||
|
||||
+130
-24
@@ -120,7 +120,7 @@ lists all additional fields. ``ref`` means ``@alias``; ``ref?`` also accepts
|
||||
- No implicit contract; ``legacy`` defaults none. OA admits Legacy only.
|
||||
* - ``oa_scenario``, ``sa_scenario``
|
||||
- ``viewpoint``: ScenarioViewpoint; ``context``: ref?; ``precondition``, ``postcondition``: strings; ``steps``: occurrence refs
|
||||
- MixedLegacy for omitted schema-2 source; none; ``""``; ``""``; ``[]``
|
||||
- MixedLegacy when viewpoint is omitted, including newly authored source 7 (see section 4.3); none; ``""``; ``""``; ``[]``
|
||||
* - ``oa_lifeline``, ``sa_lifeline``
|
||||
- ``represented``: participant, work, or role ref?
|
||||
- none
|
||||
@@ -155,37 +155,60 @@ An Enumeration contains unique string literals. For example:
|
||||
type_ref { Named @weather_classification; };
|
||||
|
||||
These are typed constructors with named fields, not embedded JSON documents.
|
||||
Constructor field names and enum spellings are case-sensitive. When replacing
|
||||
a constructor, all of its fields MUST be supplied; the containing declaration
|
||||
defaults do not silently fill a different constructor's missing fields.
|
||||
During schema-2 compatibility, the new Exchange fields ``message_kind``,
|
||||
``reply_to`` and ``values`` are exceptions: omitted values hydrate as
|
||||
Asynchronous, none and [] respectively. Canonical output writes them explicitly.
|
||||
This preserves older schema-2 source/JSON without inventing call correlation or
|
||||
value annotations. It is not a rule for an unspecified future schema.
|
||||
Constructor field names and enum spellings are case-sensitive. A selected
|
||||
constructor uses its own field contract; it does not inherit the payload of a
|
||||
different constructor from the declaration default. Required fields MUST be
|
||||
supplied. Nullability and omission are separate properties: ``?`` denotes a
|
||||
value that can be ``none``; a constructor's omission contract determines whether
|
||||
the field may be absent. There is no general all-constructor-fields-required
|
||||
rule. Other typed constructors use their contracts in the corresponding
|
||||
sections; the occurrence rules below do not establish defaults for them.
|
||||
|
||||
The following table records the source-7 OccurrenceBody contracts. Optional
|
||||
references may be omitted and then mean ``none``. Exchange also admits omitted
|
||||
``message_kind`` and ``values``, meaning Asynchronous and ``[]`` respectively.
|
||||
These omissions are accepted in newly authored source 7, not only in schema-2
|
||||
compatibility input. Earlier schema admission of constructors and fields is
|
||||
still governed by its version rules.
|
||||
|
||||
Canonical formatting writes the hydrated occurrence references and default
|
||||
``message_kind``, ``reply_to`` and ``values`` explicitly. An absent
|
||||
``operation`` remains omitted in canonical output. Lossless no-op updates and
|
||||
unrelated name edits retain authored omissions; canonical formatting is a
|
||||
different operation. An admitted incomplete occurrence can still produce model
|
||||
checks requesting its missing work, exchange, lifeline or referenced scenario.
|
||||
|
||||
.. list-table:: Occurrence body constructors
|
||||
:header-rows: 1
|
||||
:widths: 22 78
|
||||
:widths: 18 36 46
|
||||
|
||||
* - Constructor
|
||||
- Required fields
|
||||
- Fields that may be omitted, with their resulting values
|
||||
* - Exchange
|
||||
- ``exchange``: exchange relation ref?; ``sender``, ``receiver``: lifeline refs?; ``message_kind``: MessageKind; ``reply_to``: occurrence ref?; ``values``: MessageValue records
|
||||
- None
|
||||
- ``exchange``: exchange relation ref?, ``sender``, ``receiver``: lifeline refs?, ``reply_to``: occurrence ref?, ``operation``: OperationUse? → ``none``; ``message_kind``: MessageKind → Asynchronous; ``values``: MessageValue records → ``[]``
|
||||
* - Action
|
||||
- ``action``: activity/function ref?; ``lifeline``: ref?
|
||||
- None
|
||||
- ``action``: activity/function ref?, ``lifeline``: ref? → ``none``
|
||||
* - Control
|
||||
- ``kind``: Choice, Merge, Fork, Join, Loop
|
||||
- None
|
||||
* - SequenceReference
|
||||
- ``sequence``: process/functional-chain ref?
|
||||
- None
|
||||
- ``sequence``: process/functional-chain ref? → ``none``
|
||||
* - ScenarioUse
|
||||
- ``scenario``: scenario ref?; ``bindings``: LifelineBinding records
|
||||
- ``bindings``: LifelineBinding records
|
||||
- ``scenario``: scenario ref? → ``none``
|
||||
* - State
|
||||
- ``state``: state/mode ref?; ``lifeline``: ref?
|
||||
- None
|
||||
- ``state``: state/mode ref?, ``lifeline``: ref? → ``none``
|
||||
* - Fragment
|
||||
- ``operator``: Alternative, Parallel, Loop, Optional; ``branches``: branch occurrence refs
|
||||
- None
|
||||
* - Branch
|
||||
- ``guard``: string; ``occurrences``: occurrence refs
|
||||
- None
|
||||
|
||||
For example, ``body { Action { action @assess; lifeline @operator_line; }; };``
|
||||
selects an Action occurrence. Processes and functional chains contain Exchange,
|
||||
@@ -631,9 +654,23 @@ reference checks.
|
||||
Omitted schema-2 viewpoints hydrate as MixedLegacy in both source and JSON.
|
||||
This preserves pre-extension mixed work/participant scenes without discarding
|
||||
their lifelines or silently assigning a stricter meaning. MixedLegacy emits a
|
||||
review warning. New graphical factories explicitly select OperationalActivity
|
||||
or Functional by layer. Authors can select a strict viewpoint after reviewing
|
||||
the legacy scene; canonical source then records that choice.
|
||||
review warning. Source parsing also uses MixedLegacy when ``viewpoint`` is
|
||||
omitted from a newly authored source-7 scenario; it does not infer a strict
|
||||
viewpoint from the layer or lifelines. Write the intended viewpoint explicitly
|
||||
when starting a scenario. For example, this new OA source project starts an
|
||||
activity-based scenario whose lifelines and occurrences can be added later::
|
||||
|
||||
arcadia 7;
|
||||
project response "Distress response" {
|
||||
oa_scenario assessment "Assess the distress report" {
|
||||
viewpoint OperationalActivity;
|
||||
}
|
||||
}
|
||||
|
||||
An empty scenario can produce completeness warnings while remaining editable.
|
||||
The desktop's Behavior **New** flow asks authors to choose one of the layer's
|
||||
two strict viewpoints explicitly. Authors can also select a strict viewpoint
|
||||
after reviewing a legacy scene; canonical source then records that choice.
|
||||
|
||||
MessageKind is Asynchronous, Call, Reply, Create, Delete, or
|
||||
``{ Timer { expression "after 5 s"; }; }``. A process/chain exchange
|
||||
@@ -643,18 +680,28 @@ references a Call occurrence in the same scenario. The call MUST precede the
|
||||
reply on a guaranteed common sequence/enclosing branch path. Reply lifelines
|
||||
reverse the call's sender/receiver; it may reuse the call's exchange in reverse
|
||||
or use an explicit reverse-direction exchange. A second reply to the same call
|
||||
on that guaranteed path is an error. Missing reply correlation is a draft
|
||||
that can follow another reply on a shared execution path, including conditional
|
||||
or parallel operands, is an error. Missing reply correlation is a draft
|
||||
warning; non-Reply messages MUST NOT have ``reply_to``.
|
||||
|
||||
Create identifies the receiver lifeline's creation. An earlier guaranteed use
|
||||
of that lifeline is an error. Delete identifies its destruction; a later
|
||||
guaranteed use is an error. Ordering checks respect branch ownership; they MUST
|
||||
NOT flatten alternative branches into a false unconditional order. A Timer is
|
||||
Create identifies the receiver lifeline's creation. Another use that can precede
|
||||
its creation on a shared execution path is an error. Delete identifies its
|
||||
destruction; another use that can follow it on a shared execution path is an
|
||||
error. These checks include conditional and parallel operands but exclude
|
||||
mutually exclusive alternatives. Ordering checks respect branch ownership; they
|
||||
MUST NOT flatten alternative branches into a false unconditional order. A Timer is
|
||||
local to its sender lifeline, has no global exchange or values, and has either
|
||||
no receiver or the same receiver as sender. Its nonempty expression describes
|
||||
the delay/deadline; an empty expression warns. Conditions and expressions are
|
||||
stored engineering annotations, not an executable simulation language.
|
||||
|
||||
These ordering checks compare distinct stored occurrences along structural
|
||||
paths; they do not simulate repeated loop iterations. Repetition risks use
|
||||
``reply_repetition`` and ``lifeline_repetition`` warnings separately. For example,
|
||||
a loop containing one Reply to an outer Call can warn about repetition without
|
||||
containing two distinct replies; a same-operand Create/use/Delete lifetime can
|
||||
repeat without its previous iteration being treated as an earlier stored use.
|
||||
|
||||
Each MessageValue is ``{ path [@parameter]; field @data_field; expression "value expression"; }``.
|
||||
Its identity is the pair (path, field), so two uses of one Record can have
|
||||
different values for the same terminal field. Path contains enclosing DataField
|
||||
@@ -1582,6 +1629,26 @@ and Unicode escapes. Literal unescaped newlines inside strings are invalid.
|
||||
Numbers use JSON decimal/exponent syntax; NaN, infinity, hexadecimal numbers,
|
||||
leading plus signs, and leading zeroes are invalid.
|
||||
|
||||
A small model may begin with engineering declarations alone. This complete
|
||||
source describes two participants, their work, responsibility and an exchange::
|
||||
|
||||
arcadia 7;
|
||||
project distress "Distress response" {
|
||||
oa_actor vessel "Vessel in distress" {}
|
||||
oa_entity rescue "Rescue coordination" {}
|
||||
oa_activity report "Report distress" {}
|
||||
oa_activity assess "Assess incident" {}
|
||||
allocation vessel_reports "Crew reports" { source @vessel; target @report; }
|
||||
allocation rescue_assesses "Team assesses" { source @rescue; target @assess; }
|
||||
interaction distress_report "Distress report" { source @report; target @assess; }
|
||||
}
|
||||
|
||||
No diagram, placement, empty description or explicit root parent is required.
|
||||
The accepted source adds identities; keep those clauses during later edits.
|
||||
Changing ``"Assess incident"`` changes the display name while ``@assess`` still
|
||||
resolves to the same activity. Behavior-coverage warnings permit this intermediate
|
||||
model to be saved. Allocation remains explicit and distinct from decomposition.
|
||||
|
||||
Aliases match ``[A-Za-z_][A-Za-z0-9_]*``. The following aliases are reserved:
|
||||
arcadia, project, id, oa, sa, element, relation, diagram, node, artifact,
|
||||
trace, body, alias, name, description, parent, from, to, entity, actor,
|
||||
@@ -1596,7 +1663,7 @@ sets and enum constructors are closed by sections 3–5.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
document = "arcadia" "5" ";" project
|
||||
document = "arcadia" "7" ";" project
|
||||
project = "project" alias string identity? "{" project-entry* "}"
|
||||
project-entry = "description" string ";" | declaration
|
||||
declaration = element
|
||||
@@ -2005,6 +2072,45 @@ exchanges it references. Cross-layer counterparts are not owned descendants.
|
||||
Text edits describe a complete candidate and must remove or repair dependencies
|
||||
explicitly before Apply succeeds.
|
||||
|
||||
8.1. Scenario lifetime presentation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Scenario lifetime geometry is derived from the stored occurrences and their
|
||||
sequence/operand ownership. It adds no model records, source annotations or
|
||||
history entries. A lifeline's identity header and neutral editing guide remain
|
||||
available before creation, after deletion and when its represented element is
|
||||
unfinished. The guide MUST be visually distinguishable from a live interval;
|
||||
it is an editing axis, not a statement that the participant exists.
|
||||
|
||||
A directly ordered Create starts the receiver's live interval at its occurrence;
|
||||
Delete ends that receiver's interval at its occurrence. Create uses a named birth
|
||||
marker and Delete an X, each selectable through the original occurrence identity.
|
||||
The message's receiving endpoint MUST align with its marker, including self
|
||||
messages. A pre-existing lifeline without a creation has a live interval from
|
||||
below its header; without deletion it extends to the visible scenario bottom,
|
||||
including in an empty scenario. A missing lifecycle receiver MUST NOT be inferred from
|
||||
the exchange. An explicit local receiver remains meaningful when its exchange
|
||||
reference is unfinished.
|
||||
|
||||
Operands MUST be projected independently from their enclosing entry context.
|
||||
A lifetime change in an alternative, optional, loop or parallel operand MUST
|
||||
NOT imply the same change in its displayed sibling or a definite lifetime
|
||||
after the fragment. When local structure does not establish one lifetime across
|
||||
the represented paths, the continuation is visually unresolved. A subsequent
|
||||
explicit Create or Delete in the enclosing sequence can establish a definite interval.
|
||||
Neither guard text, screen order nor a guessed loop count proves execution.
|
||||
ScenarioUse remains opaque: opening its definition may show its lifecycle,
|
||||
but a callee's geometry MUST NOT create or destroy a caller lifeline.
|
||||
|
||||
Call activation is distinct from lifetime. A branch-local Delete clips that
|
||||
branch's visible activation at the death marker without inventing a Reply,
|
||||
removing an unresolved-call warning or terminating sibling paths.
|
||||
Solid activation bars are restricted to definite live intervals; an unresolved
|
||||
continuation retains its uncertainty/call diagnostic rather than depicting
|
||||
definite survival after a possible deletion. Selection,
|
||||
insertion slots, source edits and Undo/Redo retain the same semantic identities
|
||||
and validation boundary regardless of the rendered intervals.
|
||||
|
||||
9. Persistence and explicit migration
|
||||
-------------------------------------
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# Linux packaging and deployment inputs
|
||||
|
||||
The packaging command stages **already-built** desktop, server and source CLI executables for `x86_64-unknown-linux-gnu`. It runs on Windows PowerShell 5.1 or PowerShell 7 (including Linux) with Python 3.11+ standard library, Git, and the matching Rust/Cargo dependency cache. It does not build, download, install runtime libraries or launch the application. Python creates and reads back the tar archive; end users do not need Python, PowerShell or Rust.
|
||||
|
||||
Build and validate first in a source checkout. The current runtime validation baseline is Ubuntu 24.04 x86-64. A glibc symbol inventory is not a claim that every distribution with that glibc version supports the renderer and dialogs.
|
||||
|
||||
```powershell
|
||||
./scripts/package-linux.ps1 -Target x86_64-unknown-linux-gnu `
|
||||
-Version 0.2.0-rc1 -BinaryDirectory ./target/release -Python python3
|
||||
```
|
||||
|
||||
For an explicit-target build, use `target/x86_64-unknown-linux-gnu/release`. A cross-build may supply another directory containing all three executables: `arcadia-studio`, `arcadia-server` and `arcadia`. Select the Rust compiler used for the intended build before packaging; its Linux target libraries, copyright reports and locked source/archive cache must be installed. Read-only `cargo metadata --locked --offline --filter-platform x86_64-unknown-linux-gnu` and Rust version/sysroot queries are the only Rust commands run by packaging. When packaging from Windows, `-Python C:/path/to/python.exe` selects Python explicitly.
|
||||
|
||||
The script refuses existing output/claim names. It creates `dist/ArcadiaStudio-<version>-x86_64-unknown-linux-gnu/`, a `.tar.gz`, and an adjacent `.sha256`. A failed attempt leaves its claim and any partial outputs for diagnosis; choose a fresh version or output root rather than overwriting them. Archives use sorted paths, zero timestamps and uid/gid, 0755 for the three Linux executables and 0644 for other files. Every entry is read back and compared against staging, including permissions. The same unchanged inputs on the same Python/zlib implementation produce the same bytes; this does not promise reproducible compiler output.
|
||||
|
||||
## Contents and provenance
|
||||
|
||||
All three executables, the MIT license, a package-specific README, complete tracked examples (including all water-pressure teaching/evidence files), deployment references and the reachable documentation closure are included. Relative Markdown/reST/HTML file links are checked before archiving; upstream license texts are preserved verbatim and their internal source-tree hyperlinks are outside that documentation check. Untracked private files, caches and unrelated harness history are not included merely because they exist on disk.
|
||||
|
||||
`package.json` records input hashes and static ELF dependencies. `SHA256SUMS.txt` inventories every file except itself; the archive checksum covers that inventory too. `dependency-notices.json`, `THIRD-PARTY-NOTICES.txt` and `licenses/` use the same strict collector as Windows: exact target-filtered normal/build dependency graph rooted at `oa-desktop`, `oa-server` and `oa-cli`, locked crate archives, actual source hashes, checksum-bound supplements and embedded fonts, Rust distribution notices and target-library hashes. Unknown sources, absent notices or changed supplements stop packaging. No Linux system libraries are bundled by this pipeline; their runtime distribution supplies them and its notices.
|
||||
|
||||
ELF preflight requires little-endian x86-64 executables/PIE, the glibc dynamic loader, no RPATH/RUNPATH and a reviewed set of direct libc/compiler-runtime imports. Unknown libraries require an explicit packaging design instead of being silently copied from a developer machine. Graphics/window/portal libraries loaded at runtime are outside that static import list.
|
||||
|
||||
Optionally pass `-BuildEvidence <json-file>`, containing nonempty `source_identity`, `profile`, `compiler`, exact `target`, and `binary_sha256` keyed by `arcadia-studio`, `arcadia-server` and `arcadia`. Hash strings must be lowercase SHA256. Target and binary hashes are checked; other fields remain explicit caller assertions. Omission records build provenance as unknown. Never label a development binary an optimized release because of its directory or package version.
|
||||
|
||||
## Native extracted-bundle checks
|
||||
|
||||
The source authoring CLI is `./arcadia`. Run `./arcadia --help` for `check`, `lint`, `stabilize` and `rename-alias`; each takes an explicit `.arcadia` entry file. Mutation commands preview by default and require `--write` to apply their candidate; `--json` produces a machine-readable report. Test writes on a separate project copy. CLI use does not require a display, a server or Rust. Include CLI check, preview, write and unchanged-repeat checks in extracted-bundle acceptance, alongside desktop and server checks.
|
||||
|
||||
Extract into a fresh user-writable Linux directory and run from an unrelated working directory. Verify SHA256SUMS, file permissions, `file`/`ldd` and actual desktop/runtime libraries. A typical Ubuntu runtime needs X11/XKB and Wayland clients and Vulkan loader/Mesa Vulkan drivers. Linux Open/Save uses the application's path-entry dialog and does not require an xdg-desktop-portal backend. A successful `ldd` alone is insufficient because graphics/window libraries can be loaded dynamically.
|
||||
|
||||
For a software Vulkan trial, first locate the installed lavapipe ICD; only if it exists, use:
|
||||
|
||||
```sh
|
||||
WGPU_BACKEND=vulkan VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.x86_64.json ./arcadia-studio
|
||||
```
|
||||
|
||||
A compatible native GPU may run without these variables. WSLg uses real Linux user space with a particular display environment; record the actual X11/Wayland backend and do not infer support for all Linux sessions from one trial. Exercise Home walkthrough, Help/search, Linux Open/Save path entry, existing-file handling, SVG export overwrite confirmation, a multi-file project, diagram/source edits, Undo/Redo and reopening at normal/minimum window sizes. Compare complete saved model/source and inspect stderr, not merely whether a window appeared.
|
||||
|
||||
Run the server against a separate local data directory and unused loopback port. Follow [server configuration](server.md) for health, authentication, ETags, restart and backup/restore. Test create/import, a changed conditional update, unchanged no-op, stale/invalid rejection, and persistence after a clean restart. Record exact binary/archive/source/compiler hashes and platform. Packaging verification is separate from current-tree release acceptance.
|
||||
|
||||
## Container server notices and context
|
||||
|
||||
The Dockerfile is x86-64 Linux only and requires BuildKit named build contexts. Before its application build it verifies a prepared server-only notice bundle against Cargo.lock, Rust compiler release/commit, exact Linux target-library hashes, notice-file checksums and workspace source hashes. This prevents using Windows dependency notices or a stale source/compiler inventory as the container's notice claim.
|
||||
|
||||
In the source checkout, select **Rust 1.90.0** and its `x86_64-unknown-linux-gnu` target libraries to match `rust:1.90-bookworm`. Populate the locked dependency cache through your ordinary documented build/setup first; notice collection itself remains offline. On a Windows host, a Rust 1.90 GNU-host toolchain with installed Linux target libraries can provide this read-only inventory. Prepare a fresh output root:
|
||||
|
||||
```powershell
|
||||
./scripts/package-linux.ps1 -Target x86_64-unknown-linux-gnu `
|
||||
-ServerNoticesOnly -OutputRoot ./.local/container-notices-rc1
|
||||
```
|
||||
|
||||
This writes `.local/container-notices-rc1/server-notices` and a sibling claim file. Pass only the notice directory as the named context:
|
||||
|
||||
```sh
|
||||
docker buildx build --platform linux/amd64 --load \
|
||||
--build-context server_notices=./.local/container-notices-rc1/server-notices \
|
||||
-f deployment/Dockerfile -t arcadia-server:0.2.0-rc1 .
|
||||
```
|
||||
|
||||
The Dockerfile-specific ignore file allows only its ordinary source/build inputs; `.local`, model data, `.env` files and arbitrary caches are excluded from that root context. The separate named context includes only the explicitly prepared notices. The image retains them at `/usr/share/doc/arcadia-server/`, alongside Debian's own distribution notices. Do not pass a broad cache or home directory as the named context. Updating Cargo.lock, workspace sources or the compiler requires regenerating the bundle under a fresh output name.
|
||||
|
||||
The image runs as UID/GID 10001 and expects its data volume and token as described in [server configuration](server.md). Verify the actual image build, service health/authentication, writable volume and restart on a Docker-capable Linux runner. A Dockerfile review and successful local package checks do not establish container build/runtime acceptance.
|
||||
|
||||
## Runner-independent validation commands
|
||||
|
||||
Run `cargo fmt --all -- --check`, `cargo clippy --locked --workspace --all-targets -- -D warnings`, `cargo test --locked --workspace --all-targets`, and `cargo build --release --locked --workspace --bins` with the explicit supported compiler and native prerequisites. Run `python3 scripts/test-package-artifacts.py` and the PowerShell notice contract checks. Then package the resulting binaries and perform the native checks above. These commands are usable on a suitably provisioned runner; this document does not assert that GitLab, Gitea or GitHub has an active runner for this repository.
|
||||
+5
-3
@@ -1,6 +1,6 @@
|
||||
# Arcadia Studio server
|
||||
|
||||
`arcadia-server` is a headless Rust REST service for the same schema-5 model, commands, source language, validation, artifacts and OA/SA traces used by the desktop. It stores each model and its authored source project together in an atomic JSON envelope. The service provides a deployment artifact and configuration examples; this repository does not assert that a remote instance has been deployed.
|
||||
`arcadia-server` is a headless Rust REST service for the same schema-7 model, commands, source language, validation, artifacts and OA/SA traces used by the desktop. It stores each model and its authored source project together in an atomic JSON envelope. The service provides a deployment artifact and configuration examples; this repository does not assert that a remote instance has been deployed.
|
||||
|
||||
The complete HTTP contract is [api.rst](api.rst); machine-readable schemas are in [openapi.json](openapi.json), also served at `/api/v1/openapi.json`. The original [service research](research-v2-service.md) discusses broader alternatives; retained history, SQLite, idempotency receipts and stored transition plans from that research are **not** the implemented storage contract.
|
||||
|
||||
@@ -68,10 +68,12 @@ Errors use `application/problem+json` with stable `code`, HTTP `status`, human `
|
||||
|
||||
## Linux deployment
|
||||
|
||||
`deployment/Dockerfile` builds a headless image from the repository root:
|
||||
Prepare the server-only dependency notices using the pinned Rust toolchain as described in [Linux packaging](linux-packaging.md), then build the headless image from the repository root. BuildKit supplies this checksum-bound notice bundle as a separate context; the ordinary build context excludes local caches and model data.
|
||||
|
||||
```sh
|
||||
docker build -f deployment/Dockerfile -t arcadia-server:local .
|
||||
docker buildx build --platform linux/amd64 --load \
|
||||
--build-context server_notices=./.local/container-notices-rc1/server-notices \
|
||||
-f deployment/Dockerfile -t arcadia-server:local .
|
||||
docker run --rm --name arcadia-server \
|
||||
-p 127.0.0.1:8787:8787 \
|
||||
-v arcadia-data:/data \
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# Source CLI
|
||||
|
||||
Use `arcadia` to check or update local `.arcadia` projects without opening Studio
|
||||
or starting a server. Pass the entry file explicitly; its relative include paths
|
||||
resolve from their containing source files.
|
||||
|
||||
Build with `cargo build --locked -p oa-cli --bin arcadia`. The resulting command
|
||||
is `target/debug/arcadia.exe` on Windows or `target/debug/arcadia` on Linux unless
|
||||
your Cargo configuration chooses another target directory. A workspace release
|
||||
build also includes it. Use the platform's usual executable path prefix when
|
||||
running these examples.
|
||||
|
||||
```sh
|
||||
arcadia check project.arcadia
|
||||
arcadia check project.arcadia --json
|
||||
arcadia stabilize project.arcadia
|
||||
arcadia stabilize project.arcadia --write
|
||||
arcadia rename-alias project.arcadia old_alias new_alias
|
||||
arcadia rename-alias project.arcadia old_alias new_alias --write --json
|
||||
```
|
||||
|
||||
`lint` is an alias for `check`. Checks accept incomplete engineering models with
|
||||
warnings; syntax errors and structural violations fail. Located diagnostics
|
||||
refer to the named file and coordinate space. Line and column numbers are
|
||||
one-based and columns count Unicode scalar values; byte ranges are half-open
|
||||
UTF-8 offsets.
|
||||
|
||||
`stabilize` previews missing persistent identities and any supported source
|
||||
migration. `rename-alias` changes one existing global alias and its typed
|
||||
references while preserving comments, strings and unrelated source. Pass alias
|
||||
names without `@`. The command does not rename an element's display name.
|
||||
|
||||
Both editing commands preview by default. Human output writes unified file
|
||||
diffs to stdout and diagnostics to stderr. `--json` writes one versioned report
|
||||
with diagnostics, migration information, exact before/after text for every
|
||||
changed file, diffs and the write result. Unchanged files are not included in
|
||||
that report. They are still checked for conflicts before a write.
|
||||
|
||||
Add `--write` to save the candidate prepared by that invocation. Even an empty
|
||||
diff checks the captured files and pending-save state before returning. A failed
|
||||
write reports `written: false` and retains its proposed diff. This does not
|
||||
guarantee rollback; the reported conflict or recovery outcome determines the
|
||||
next step. A pending save
|
||||
requires explicit recovery through the existing project recovery workflow;
|
||||
the CLI does not recover it automatically.
|
||||
|
||||
Missing IDs in a separate earlier preview remain provisional. A later command
|
||||
may allocate different IDs until you save them. This CLI does not persist a
|
||||
review plan for later application. It does not canonicalize the whole project
|
||||
or delete detached files. Source versions 3–7 use the core's supported migration
|
||||
rules; invalid source is not rewritten.
|
||||
|
||||
| Exit code | Meaning |
|
||||
| --- | --- |
|
||||
| 0 | Valid check or successful preview/write; warnings may remain |
|
||||
| 1 | Invalid source or refused semantic alias change |
|
||||
| 2 | Invalid command arguments |
|
||||
| 3 | File/path, conflict, pending-save or output failure |
|
||||
|
||||
Admission bounds match the desktop: 256 files, include depth 32, 1024 include
|
||||
edges, 64 MiB total UTF-8, 100,000 graph objects, 250,000 records and 500,000
|
||||
ordered references. `arcadia --help` lists the commands and bounds. These limits
|
||||
are not a claim of interactive performance at the maximum project size.
|
||||
|
||||
For the notation itself, start with [the text-first examples](../examples/text-first/README.md)
|
||||
and the [language contract](language.rst).
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
The standard build target is x86_64 MSVC. The optional gnullvm build uses LLVM-MinGW UCRT and currently needs `libunwind.dll` alongside both executables. Packaging is a separate operation on already-built inputs; it neither builds nor launches applications and does not confer release acceptance.
|
||||
|
||||
Run from a Git checkout with Windows PowerShell 5.1 or PowerShell 7. Build and test first using the [README](../README.md). Select the actual output directory explicitly; a native-host Cargo build normally uses `target/release`, while an explicit `--target` uses `target/<target>/release`.
|
||||
Run from a Git checkout with Windows PowerShell 5.1 or PowerShell 7 and Python 3.11+ (standard library only). Build and test first using the [README](../README.md). Select the actual output directory explicitly; a native-host Cargo build normally uses `target/release`, while an explicit `--target` uses `target/<target>/release`. `-Python <executable>` selects Python when it is not named `python` on PATH; it collects/checks the package-local documentation closure without running binaries.
|
||||
|
||||
Packaging also needs the build's matching `cargo` and `rustc` on the packaging process PATH, the locked dependency sources/archive cache, and installed Rust target libraries and copyright reports. The script invokes only read-only `cargo metadata --locked --offline --filter-platform <target>` and Rust version/sysroot queries. It does not download dependencies or build. These tools are packaging prerequisites; the extracted application does not require them.
|
||||
|
||||
```powershell
|
||||
# Standard MSVC outputs, already built:
|
||||
# MSVC outputs, already built with a verified runtime strategy:
|
||||
./scripts/package-windows.ps1 -Target x86_64-pc-windows-msvc `
|
||||
-Version 0.2.0-rc1 -BinaryDirectory ./target/release
|
||||
|
||||
@@ -17,11 +17,13 @@ Packaging also needs the build's matching `cargo` and `rustc` on the packaging p
|
||||
-LlvmMinGWRoot C:/Tools/llvm-mingw-20260826-ucrt-x86_64
|
||||
```
|
||||
|
||||
The caller must supply the matching build target, version, and final binaries. PE headers establish x86_64 architecture, but do not prove a source revision or Cargo profile. `package.json` records input hashes and inspected imports; retain build/test evidence tied to those hashes separately. The version labels the package and does not change the embedded application version.
|
||||
The caller must supply the matching build target, version, and final binaries. PE headers establish x86_64 architecture, but do not prove a source revision or Cargo profile. `package.json` records input hashes and inspected imports; retain build/test evidence tied to those hashes separately. The version labels the package and does not change the embedded application version. Optional `-BuildEvidence <json>` requires nonempty `source_identity`, `profile`, `compiler`, exact `target`, and `binary_sha256` keyed by both executable filenames. Target and lowercase SHA256 values are checked; source/compiler/profile remain explicitly supplied assertions, never inferred from a directory name.
|
||||
|
||||
Ordinary dynamic-CRT MSVC output can import VC redistributables and will be refused by the strict runtime check. One candidate recipe is an explicit-target build with `CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS=-C target-feature=+crt-static`; validate its actual imports and runtime on the intended platform before packaging. This guide does not assert that such a build has been performed. The existing GNU/LLVM recipe has its own exact libunwind staging below. Do not silence an unknown import or copy arbitrary DLLs to make packaging pass.
|
||||
|
||||
The script creates a fresh `dist/ArcadiaStudio-<version>-<target>/`, matching ZIP, and ZIP `.sha256` file. `-OutputRoot` selects another location. Existing output names are refused; no directories are deleted or overwritten. A `.packaging.lock` claim and partial outputs remain after failure for inspection; retry under a fresh version/output location. ZIP file order and timestamps are fixed so unchanged inputs can be compared across runs; this is not a reproducible compiler-build guarantee.
|
||||
|
||||
Each package includes desktop and server executables, README, MIT license, built-in Help in the application, tracked docs (including language/API specifications and OpenAPI), tracked examples and deployment recipes, current coverage/runtime notes, START-HERE, provenance, and SHA-256 inventory. It includes current working-tree contents of tracked files, so review uncommitted documentation edits before packaging. Untracked sample/user files are excluded. Repository sources are needed separately for the documented Cargo and Docker build recipes.
|
||||
Each package includes both executables, a package-specific README, MIT license, built-in Help, language/API specifications and OpenAPI, server/platform guides and their reachable documentation closure, all tracked examples and deployment references, START-HERE, provenance, and SHA256 inventory. Relative Markdown/reST/HTML file links are checked before archiving; upstream license texts retain their own source-tree links unchanged. Current working-tree contents of tracked inputs are copied, so review uncommitted documentation edits before packaging. Untracked sample/user files are excluded. Repository sources and tools are needed separately for Cargo/Docker recipes; the archive README makes this distinction explicit.
|
||||
|
||||
For gnullvm, the exact selected toolchain's x86_64 `libunwind.dll` and full distribution `LICENSE.TXT` are included; both are hashed. Standard MSVC packaging has no LLVM-MinGW coupling. The script reads ordinary PE imports recursively for packaged runtime DLLs, accepts a bounded list of Windows system components/API sets, and fails on unknown imports. For example, a build importing a VC redistributable DLL must have its redistribution and dependency handling resolved before packaging; it is not silently treated as part of Windows. Do not copy arbitrary DLLs from System32. Dynamically loaded graphics libraries and delay-load imports require the runtime check below.
|
||||
|
||||
@@ -64,6 +66,6 @@ try {
|
||||
}
|
||||
```
|
||||
|
||||
This snippet only starts processes; it does not verify readiness. Check the server's health route documented in [server setup](server.md), import/read/update a model, restart with the same temporary store, and verify persistence. Close only the processes started for this smoke. Exercise the desktop's Home/example/open/save/reopen and Help, inspect real diagrams, and perform the [Save-as Undo/Redo recheck](../harness/release-save-as-runtime-packaging.md). Close normally and check stderr/exit state. A process that survives a few seconds alone is not adequate GUI evidence.
|
||||
This snippet only starts processes; it does not verify readiness. Check the server's health route documented in [server setup](server.md), import/read/update a model, restart with the same temporary store, and verify persistence. Close only the processes started for this smoke. Exercise Home/example/open/save/reopen and Help, inspect real diagrams, and verify a graphical edit → Save as → Undo → Redo → Save → reopen against the complete intended model/source. Close normally and check stderr/exit state. A process that survives a few seconds alone is not adequate GUI evidence.
|
||||
|
||||
Acceptance is pending until these checks are actually performed against the packaged final binaries. Historical V2 bundles, this machine's Cargo checks, and a successful development launch with compiler PATH are separate evidence.
|
||||
|
||||
@@ -65,12 +65,12 @@ The integration oracle executes this exact semantic review and source update. It
|
||||
| `oa/`, `sa/` | Participants, work, data, capabilities and reusable behavior |
|
||||
| `views/` | Five saved views; filters and placements are presentation state |
|
||||
| `traceability.arcadia` | OA↔SA correspondence and review history |
|
||||
| `model.json` | Current schema-6 interchange of the final accepted model |
|
||||
| `model.json` | Current schema-7 interchange of the final accepted model |
|
||||
| `transition-before.json`, `transition-request.json` | Explicit first-transition replay inputs |
|
||||
| `evidence/repair-check.md` | Local teaching evidence linked from the SA conversation |
|
||||
| `model.rs` | Deterministic lesson builder shared by generator and tests |
|
||||
|
||||
The REST routes are documented in [the API reference](../../docs/api.rst). Import `model.json` with `POST /api/v1/projects/import`; query the returned resource's `/model` and `/validation`. Expect schema 6 and the one warning above. JSON import provides canonical source; to retain the exact modular source layout, use `/api/v1/projects/import-source-project` with a SourceBundle containing entry `project.arcadia` and only the 30 reachable `.arcadia` file strings, keyed by slash-separated relative paths. Neither route reads the artifact target from your local filesystem.
|
||||
The REST routes are documented in [the API reference](../../docs/api.rst). Import `model.json` with `POST /api/v1/projects/import`; query the returned resource's `/model` and `/validation`. Expect schema 7 and the one warning above. JSON import provides canonical source; to retain the exact modular source layout, use `/api/v1/projects/import-source-project` with a SourceBundle containing entry `project.arcadia` and only the 30 reachable `.arcadia` file strings, keyed by slash-separated relative paths. Neither route reads the artifact target from your local filesystem.
|
||||
|
||||
For transition replay, import `transition-before.json` instead. Submit `transition-request.json` to that baseline resource's `/transitions/preview` with its current ETag in `If-Match`. Review the returned plan, then follow the documented `/transitions/apply` protocol. Use the baseline resource for this exercise: the final model already contains the mapped records and subsequent refinements. This tutorial's automated evidence is core/source validation; it does not claim a separate native or HTTP acceptance run of this particular example.
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# Start a model in text
|
||||
|
||||
Open `project.arcadia`, then choose **Textual model**. The model describes who
|
||||
reports distress, who assesses it, and the information passing between those
|
||||
activities. It needs no diagram or handwritten UUIDs.
|
||||
|
||||
1. Read each declaration as **kind, alias, display name**. References such as
|
||||
`@assess` use the alias. The two allocations state responsibility; the
|
||||
interaction states the direction of the exchange.
|
||||
2. Change `"Assess incident"` to a name suited to your project. Use **Apply all**,
|
||||
then **Save all**. Keep the identity clauses added to the accepted source:
|
||||
they preserve the same model objects in later edits.
|
||||
3. Add work and exchanges as your understanding grows. Behavior coverage checks
|
||||
identify activities and exchanges that still need process or scenario context;
|
||||
these advisory checks allow you to save the intermediate model.
|
||||
|
||||
Changing a display name keeps references intact. Changing an alias requires
|
||||
updating every `@alias` use. Nesting expresses decomposition; an allocation
|
||||
expresses who performs work. Neither implies the other.
|
||||
|
||||
For a larger example with behavior, ports, views and OA/SA correspondence, use
|
||||
[the water-pressure walkthrough](../release1/README.md). The
|
||||
[language reference](../../docs/language.rst) defines all declarations and the
|
||||
rules for splitting a project into included files.
|
||||
@@ -0,0 +1,10 @@
|
||||
arcadia 7;
|
||||
project distress "Distress response" {
|
||||
oa_actor vessel "Vessel in distress" {}
|
||||
oa_entity rescue "Rescue coordination" {}
|
||||
oa_activity report "Report distress" {}
|
||||
oa_activity assess "Assess incident" {}
|
||||
allocation vessel_reports "Crew reports" { source @vessel; target @report; }
|
||||
allocation rescue_assesses "Team assesses" { source @rescue; target @assess; }
|
||||
interaction distress_report "Distress report" { source @report; target @assess; }
|
||||
}
|
||||
@@ -278,3 +278,71 @@ This is a selected design under implementation, not a native acceptance claim.
|
||||
Native reverse scenario Apply selected a dependency and inserted five overlapping behavior placements into an unrelated architecture view. Core transition correctness did not make that presentation useful. Select the counterpart of the explicit request root and resolve its nearest scenario/process/chain/state owner. Without an explicit destination diagram, keep the exact core candidate and open Behavior; preserve unrelated architecture views. Retain ordinary architecture transition placement and honor explicit diagram choices.
|
||||
|
||||
Automatically creating another architecture view would retain the wrong editing workspace and introduce unrequested model state. Merely navigating to Behavior after adding placements would conceal the presentation damage. Both alternatives are rejected. The selected correction retains one source transaction and Undo operation and guards stale canvas events. Revisit if mixed-root requests or realistic explicit destination workflows require a separate user choice; do not infer such a choice from dependency ordering. Test/build/native acceptance is recorded separately in release-behavior-transition-context-20260906.md.
|
||||
|
||||
## Property baselines and explicit alternative cases — 2026-09-11
|
||||
|
||||
Problem: complete independent bindings repeat shared engineering assumptions in
|
||||
every ordinary study case. The user requires practical text-only development and
|
||||
reduced cognitive load for small and large models. Evaluator implementation
|
||||
simplicity alone cannot justify that authoring cost.
|
||||
|
||||
Select property-owned base values/formulas and one-level case overrides as the next
|
||||
active-study contract to formalize. Every evaluator caller explicitly requests
|
||||
ModelBaseline or a particular Case identity; no active UI selection, unnamed
|
||||
default case or case-to-case inheritance determines meaning. The baseline is the
|
||||
model's explicit property values, not a mandatory extra record for entering the
|
||||
first value. Inherited formulas read the requested effective case. An explicit
|
||||
Unbound override shadows a known base; clearing an override uses the base.
|
||||
|
||||
The [executed comparison](release-analysis-context-experiment-20260911.md) passed
|
||||
218 assertions. Twenty cases with20 fixed/two varying inputs used62 binding rows
|
||||
under B versus480 under D, with identical results. Updating one shared assumption
|
||||
required one B binding versus20 D bindings. Reusable predicates eliminated D's
|
||||
threshold-edit disadvantage, so repeated formulas are not the reason for selection.
|
||||
|
||||
Retain an explicit complete independent freeze/export for intentionally separate
|
||||
assumptions. It must capture effective values/formulas and the full exact definition
|
||||
environment, including package pins; copying only case rows leaves hidden live
|
||||
dependencies. Captured revisions and per-input origins must make propagation
|
||||
visible. Effective dependency/cycle validation and stale result handling must cover
|
||||
the whole requested context.
|
||||
|
||||
Reject compulsory complete cases for ordinary shared-assumption modeling; retain
|
||||
their useful isolation as an explicit independent snapshot. Reject arbitrary
|
||||
inheritance and UI-dependent evaluation. Do not import the tiny experiment
|
||||
interpreter as production architecture. Exact check identity, type/operator
|
||||
algebra, source syntax, library closure/compatibility, transitions and public
|
||||
snapshot formats still require the coordinated precise contract. Current source7
|
||||
does not implement this decision.
|
||||
|
||||
Revisit if complete text/native tasks expose unacceptable hidden propagation,
|
||||
wrong-context edits or inability to preserve reproducible independent studies.
|
||||
The comparison establishes semantic/text maintenance evidence, not human task
|
||||
timings or full quantity/library integration acceptance.
|
||||
|
||||
## Scenario lifetime rendering integration — 2026-09-11
|
||||
|
||||
Codex-ytr83r /root. Adopt the [scoped lifecycle proposal](release-scenario-lifecycle-design-20260910.md)
|
||||
as derived presentation over existing source-7 semantics. Always retain a neutral
|
||||
identity header/editing guide; depict definite live intervals separately, with
|
||||
original-occurrence Create capsules and Delete crosses. Alternative, optional,
|
||||
loop and parallel operands inherit their enclosing entry independently and leave
|
||||
an unresolved continuation after a local lifetime change. Reused scenarios remain
|
||||
opaque. This does not execute guards or add model/source/history records.
|
||||
|
||||
Full-height ordinary spines fail to communicate creation/deletion; one global
|
||||
earliest-birth/latest-death range would falsely order exclusive operands. Both
|
||||
alternatives lose to the scoped projection. The [independent review](release-scenario-lifecycle-independent-review-20260911.md)
|
||||
requires true visible root bounds, a complete rendering lane map and local
|
||||
activation clipping at Delete; insertion bounds/eligible lanes must keep their
|
||||
separate roles. Receiver endpoints and selectable markers must agree, including
|
||||
self messages. Those findings determine the integration contract in language
|
||||
section 8.1 and the real-Scene/native acceptance fixtures.
|
||||
|
||||
Core/source semantics are unchanged. In section 4.3, replacing the old
|
||||
guaranteed-only lifetime/duplicate-reply wording with possible shared-path wording
|
||||
corrects documentation to `validation.rs` and the existing conditional/parallel
|
||||
regressions; it does not broaden admission or add a new rule. Compilation and
|
||||
native acceptance of the renderer are still pending at this decision. Revisit the
|
||||
visual encoding if actual minimum-size engineering workflows make marker identity,
|
||||
uncertain continuation or the distinction between guide and existence unclear.
|
||||
|
||||
+67
-107
@@ -1,117 +1,77 @@
|
||||
> Superseded on 2026-09-11. This document is a preserved historical checkpoint.
|
||||
> Live plans, priorities, ownership and handoffs now reside in [Gitea issue 1](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/1#issuecomment-410) and [issue 2](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/2).
|
||||
> Do not resume synchronizing this ledger. See [STATE.md](STATE.md) for the integrated checkpoint.
|
||||
|
||||
# First release execution plan
|
||||
|
||||
## Active execution — 2026-09-10
|
||||
Updated 2026-09-11 for the published Codex-ytr83r B4 source checkpoint.
|
||||
[PROJECT_GOAL.md](../PROJECT_GOAL.md) defines the full outcome;
|
||||
[STATE.md](STATE.md) scopes evidence/ownership and
|
||||
[TASK_LEDGER.md](TASK_LEDGER.md) separates facts from decisions.
|
||||
|
||||
1. Restore a reproducible local toolchain; repair the interrupted compact palette;
|
||||
verify focused history/draft/placement regressions and the actual native workflow.
|
||||
2. Independently reassess current behavior/scenario ergonomics and OA/SA semantic,
|
||||
source/API and documentation completeness. Resolve verified high-impact gaps
|
||||
with bounded changes and cross-representation acceptance criteria.
|
||||
3. Complete realistic graphical OA/SA teaching, transition, behavior, recovery and
|
||||
hosted workflows. Exercise minimum and wide layouts, rejected edits and history.
|
||||
4. Run current Windows/Linux automated and native acceptance, fresh independent
|
||||
end-state reviews, optimized builds and package verification. Publish accurate
|
||||
build/run evidence and handoff only when the original release criteria hold.
|
||||
## Immediate integration and verification
|
||||
|
||||
This ordering supersedes historical stopped-session next actions below. Preserve
|
||||
their scoped evidence; no test or runtime result transfers to a new binary without
|
||||
verification. Root holds the serialized build/native lease; Gitea #1 tracks workers.
|
||||
1. Preserve completed integration c81e285/e207b04/b853a1e and reviewed B4 source
|
||||
52f6e17 on codex/release1-ytr83r. Do not replay equivalent3978/d114 changes.
|
||||
CLI-only01b76a1 was reviewed and imported as023e3a8 without core/dependency
|
||||
changes. Preserve the new AGENTS maintenance instruction and exact goal bytes.
|
||||
2. Core433, independent lifecycle-oracle admission, focused lifecycle22 and full
|
||||
desktop675 passed on this host. Production freeze2908405 is unchanged by
|
||||
evidence checkpoint3ade397. GUI build, both-layer history/reopen and corrected
|
||||
workspace strict Clippy passed. Finish the affected test and Unicode/native
|
||||
workflows below. Do not repeat the passed suites without a
|
||||
source change or new finding. Root serializes writers and records exact scope.
|
||||
3. Use the imported evidence2ec creation-oracle v2 for native Undo comparisons.
|
||||
Reject unknown/changed detached files while allowing exact files
|
||||
retained from an explicit pre-Undo origin. Preserve full Project and reachable
|
||||
SourceBundle equality and original role identities through Redo/reopen.
|
||||
4. Exercise current Windows UI at minimum/wide sizes: creation name/owner/viewpoint,
|
||||
one-step history, stale/raw-draft rejection, first Escape/modal priority,
|
||||
recovery, clicked root/ALT action/observation/reuse, exact caller return,
|
||||
same-size Fit and natural subsequent scrolling. Use independent semantic
|
||||
predictions, not only screenshots or serialization of observed outputs.
|
||||
5. Review current external Gitea handoffs before importing staged prerequisites,
|
||||
empty-state actions, contextual chooser or source CLI. Validate final combined
|
||||
workflows and preserve explicit ownership/qualification semantics.
|
||||
6. After actual B4 test/native acceptance, prepare a distinct runnable Windows
|
||||
prerelease with current-toolchain notices and exact executable/source hashes.
|
||||
Existing MCP credentials have now completed a scoped branch push and remote
|
||||
readback; release attachment upload still needs concrete-artifact verification.
|
||||
Codex-9e8r6t retains the earlier named-authoring prerelease ownership.
|
||||
7. Linux setup/downloads/builds/checking are paused by direct user instruction.
|
||||
Wait for the user to resolve WSL2 networking and explicitly instruct resumption.
|
||||
Preserve installed Ubuntu, exact VirtioProxy configuration and complete/partial
|
||||
offline inputs. Networking remains unresolved; preparation is not Linux
|
||||
acceptance. Follow the current WSL/offline records rather than old reboot plans.
|
||||
8. Incorporate the owner clarification in issue2: process/scenario interaction
|
||||
should use consistent direct canvas workflows while preserving domain
|
||||
constraints. Codex-956atf owns the shared canvas design; B4 rendering and
|
||||
prerequisite forms support it but do not close this requirement.
|
||||
|
||||
**Latest refinement:** consult actual installed Capella 7.1, replace the user-rejected horizontal creation/connection rows with a compact contextual palette and inline authoring, independently critique interaction risks, then build and verify the real workflow at wide/minimum sizes. [Benchmark and concrete acceptance](release-capella-basic-editing-20260907.md). Do not ship the rejected toolbar as the usability correction.
|
||||
## Capability milestones
|
||||
|
||||
**Current priority (7 September):** the user's usability correction takes precedence over the advanced feature sequence below. Establish contextual named diagram creation, existing-element placement, basic editing and clear layer transitions, then verify them natively. Desktop checks were explicitly reauthorized. See [priority reset and acceptance](release-basic-editing-reset-20260907.md).
|
||||
| Outcome | Work and acceptance |
|
||||
| --- | --- |
|
||||
| Usable direct behavior/state engineering | Continuous ordered/explicit process authoring, clicked root/nested scenario insertion, exact reuse bindings/caller return, state naming/creation, recovery and source-preserving history. Complete B4 lifetime rendering with actual lifetime bounds, branch-local Delete activation clipping, incomplete lanes and self-arrow endpoints. |
|
||||
| Precise engineering analysis | Refine property-owned baseline values/formulas plus explicit ModelBaseline/Case one-level overrides. Close quantity kind/unit/value, exact/approximate numerical policy, dependencies/cycles, false/missing/error, complete freeze and definition provenance. Implement coherent validated core/source/REST/GUI/history contracts. An exact-duration slice alone cannot satisfy the full goal. |
|
||||
| Maintained reusable libraries | Resolve source-only normalization, pinned offline definitions/closure, provenance versus checked same-origin compatibility, source→local instance maps, explicit three-way updates/local divergence, subjectless analysis closure and OA/SA mounted-data transition policy. Demonstrate two projects/two instances, cosmetic and semantic upgrades, conflicts/detach and rollback. |
|
||||
| Practical complete textual and graphical tasks | Measure small/large text-only creation, cross-file reference navigation, safe alias rename and lossless patch preview. Integrate source CLI and bounded contextual choices; actual keyboard/IME, repeated names, stale events, recovery and minimum geometry must pass. Keep offline guide/examples and normative source documentation aligned. |
|
||||
| Complete engineering stories | Close OA↔SA transition/reconciliation, typed data/interfaces/state, artifacts, saved views, hosted workflows and tutorial tasks against the full current goal. Confirm parity and precise specification rather than inheriting historical exclusions. |
|
||||
| Reproducible Windows/Linux release | Current tests/format/strict Clippy, reST/OpenAPI/migration/example gates; real GUI and REST transport/restart/ETag conflict acceptance; matching optimized desktop/server packages, complete notices/archive hashes and clean-runtime launch. Fresh independent requirements/domain/parity/architecture/UX/runtime review resolves material findings before release. |
|
||||
|
||||
## Outcome and baseline
|
||||
Milestones run adaptively where independent work helps. Analysis and libraries
|
||||
share definition/identity assumptions and must converge before broad migrations.
|
||||
Other-host slices remain coordinated through issue #1 and Wiki. Native/Cargo
|
||||
leases are host-local; source ownership and exact transfer heads are shared.
|
||||
|
||||
Deliver the first release of Arcadia Studio for system engineers: complete Operational Analysis and System Analysis/System Architecture workflows, equivalent graphical and textual models, clear artifact traceability and a deployable/programmatically accessible Rust service. Logical/Physical Architecture and later Arcadia layers are future releases, as the new objective explicitly states.
|
||||
## Evaluator boundaries and replanning
|
||||
|
||||
The authoritative objective is attachment `10b0f701-1743-4de7-9ee1-225920e19649/goal-objective.md`. It retains first-release scope and strengthens domain coherence, alternative evaluation and target-user task evidence. TASK_LEDGER.md separates given/verified facts, assumptions and open candidates; STATE.md records progress. The accepted 0.2.0 prototype is a historical baseline, not proof of release completeness. Its old D:/CodexBuilds archives are unavailable on this machine; existing dist archives must not be replaced until new acceptance passes.
|
||||
Use authoritative source semantics, exact UUID/source preservation and actual
|
||||
user workflows. Source-only experiments are not accepted new grammar; mounted
|
||||
widget tests are not native readability; synthetic packaging checks are not
|
||||
Linux runtime. Preserve old environmental failures as evidence without inventing
|
||||
current blockers. Continue independent work when platform or transfer work waits.
|
||||
|
||||
The user's cross-branch reminder led to an ancestry/patch/tree audit. Continue from integrated `c091f80` on `codex/release1-continuation`; direct state/scenario/process, data, hosted and semantic review work already exists here. `release-branch-integration-20260906.md` records the audit. `release-current-coverage-20260906.md` is the current measurable acceptance map and supersedes stale absence claims in older narratives below.
|
||||
|
||||
## Existing architecture and retained decisions
|
||||
|
||||
Retain three Rust crates and one typed Project/validated atomic Command boundary. oa-core owns semantics, references, history, transitions, source and persistence; oa-desktop owns Iced interaction/drafts/projections; oa-server owns REST/ETag/current-model storage. Keep identity, semantic containment, allocation and per-diagram placement separate. No runtime plugin framework or later-layer skeleton.
|
||||
|
||||
The current forms are a closed typed-record adapter, not a second metamodel. Expand their real creation/reference/variant controls with every semantic addition. Core choices must drive graphical choices. New persistent fields participate in text round trips, identity/removal visitors, transitions, API schemas and graphical authoring tests.
|
||||
|
||||
## Milestones and measurable acceptance
|
||||
|
||||
Nested source ownership and persistent REST bundles are implemented and verified: Windows minimum-size source-to-graphics, nested reparent/source/Undo/Redo/Save/reopen, recovery, rebuilt Linux source/history/Save/reopen, independent review and both platform suites passed. Preserve the existing grammar/updater and separate allocation/order semantics.
|
||||
|
||||
Explorer/port discovery, custom routing, direct palette connections, four-side port
|
||||
anchors, contextual menus and source folders have scoped Windows/Linux acceptance.
|
||||
See release-explorer-usability.md, release-routing-verification.md,
|
||||
release-routing-linux.md and release-port-drawing.md for exact evidence and limits.
|
||||
|
||||
Direct state-machine editing is accepted through316dc55: typed persisted views,
|
||||
explicit-owner creation, movement, direct transitions/reconnection, typed detail
|
||||
forms, folding, placement and correct camera/tool lifetime. Windows default/minimum
|
||||
and Linux native acceptance plus independent reviews are recorded in
|
||||
release-state-editing.md. Direct scenario/process gestures remain required.
|
||||
|
||||
R6 data contracts and direct scenario/process gestures are integrated; native
|
||||
acceptance continues on both platforms. Typed R3 core/source5/REST and scalar review
|
||||
milestones1–2 are integrated and pass full platform checks. Close independent scalar
|
||||
fixes and native review, then finish constructor/keyed/order/reset/artifact and
|
||||
multi-source graphical authoring using the existing shared core planner. Source5
|
||||
migration retains lexical ownership/comments; no flat source rewrite is allowed.
|
||||
See release-data-contract.md, release-semantic-integration.md and release-semantic-gui.md.
|
||||
|
||||
Continue source authoring improvements only through the production parser/updater.
|
||||
The23-test experiment is evidence, not architecture: independent probes exposed
|
||||
comment misattachment and unsupported edits. Additional shorthand must preserve
|
||||
complete edit/delete behavior and exact source history, and justify itself in real
|
||||
human authoring tasks. Shared state placements are now validated; scenario/chain
|
||||
interaction must preserve its own occurrence and ordering semantics.
|
||||
|
||||
1. **Release coverage audit and interfaces.** Compare actual core/UI/API with official Capella OA/SA semantics and the new objective. Freeze a coverage table and concrete variant/reference/transition contracts before editing shared interfaces. Distinguish unsupported later layers from missing OA/SA workflows; V2 exclusions are reconsidered. Native audit exercises onboarding/help/authoring at supported sizes. Independent audits have separate file ownership.
|
||||
2. **Complete engineering semantics.** Fill verified gaps in capability relationships and mission involvement; operational process/functional-chain function/control topology; scenario/state/data constructs needed by OA/SA engineers. Add typed semantic-content drift and reviewed existing-counterpart reconciliation, including reference mapping and explicit dependency/ownership decisions. Every addition has a usable graphical authoring workflow, equivalent source/API representation, meaningful reject/undo/delete/reidentify/transition tests and a worked example. Exact additions and invariants are recorded in the coverage/interface artifact from milestone1, avoiding speculative LA/PA concepts.
|
||||
3. **Approachable authoring and help.** Replace static help with searchable topic navigation, concise concepts, numbered OA→SA tutorials, related topics and direct workspace actions. Add a focused starting workspace, recent models and practical creation guidance. Improve source authoring with asynchronous versioned lint, semantic warnings, diagnostic navigation and find/go-to tools. Make selection/navigation and hidden/placed model distinctions clear. Add recoverable local checkpoints for unsaved accepted models and drafts; preserve explicit file-save/conflict behavior.
|
||||
4. **Connected automation workflow.** Keep headless deployment and shared core REST contract. Add direct optional desktop open/publish/save of hosted models using explicit connection details and conditional ETags, preserving drafts on failure/conflict and keeping credentials in memory. Verify actual desktop→API→desktop edits and artifact integration locally. Update OpenAPI, deployment and source contracts for the expanded release semantics.
|
||||
5. **Release integration and polish.** Guided examples cover OA goals/roles/process alternatives, system missions/capabilities/boundary contracts, chains/scenarios/states and evidence. Review default/minimum/wide layouts, practical keyboard paths, empty/loading/error/recovery states and performance on representative models. Build/run the actual native app after each meaningful UI milestone, fix from evidence, then reverify.
|
||||
6. **Independent final acceptance and packaging.** Fresh requirements, correctness, native UX and simplicity reviews in parallel; fix all material findings and rerun affected checks. Full Windows/Linux locked tests, formatting, strict Clippy and optimized builds. Operate primary GUI workflows and actual service transport. Verify current docs/reST rendering, examples/migration, reproducible source workflow, package binary equality/archive contents and checksums. Only then replace 0.2.0 bundles and mark this goal complete.
|
||||
|
||||
Dependencies: milestone1 freezes shared interfaces for2; guide/lifecycle work in3 can proceed independently once UI ownership is partitioned. Structural counterpart work depends on2. Hosted client4 depends on final core/API types but transport/lifecycle can be developed independently. Milestones5–6 integrate all preceding work.
|
||||
|
||||
## UI and state flow
|
||||
|
||||
Keep task navigation, engineering perspective and context-sensitive properties. A Home/start workspace offers projects, recovery and tutorials without obscuring active modeling. Help uses local Rust-owned content and search/category navigation; workspace links pass explicit events and respect draft/modal guards.
|
||||
|
||||
Text edits update a snapshot/version; parsing/semantic lint runs on a blocking worker and only the latest result is adopted. Syntax errors and semantic warnings have distinct severity/location and navigate to source. Applying text remains one atomic model transaction. Invalid text and property input are recoverable drafts.
|
||||
|
||||
Recovery writes separate per-session checkpoints in user-local application data, never silently overwrites a model file, and offers explicit restore. Recent paths are convenience metadata. Hosted models use a separate connection/resource/revision/ETag handle; ordinary core editing and Undo stay local until explicit conditional Save. Network/file failures retain the same draft. Tokens are not persisted or included in diagnostics.
|
||||
|
||||
## Failure and verification strategy
|
||||
|
||||
Cover wrong-layer/reference/kind input, ownership/reference cycles, duplicate or ambiguous counterpart matches, stale plans/ETags, typed semantic drift, sequence control topology and repeated occurrences, invalid source/property drafts, external file edits, failed storage/network operations, oversized models and unavailable graphics. Keep incomplete engineering work editable through warnings while rejecting structural corruption.
|
||||
|
||||
Tests must cover user-visible meaning and cross-representation invariants, not just serialize the same value twice. Use actual HTTP and native CUA for end-to-end behaviors. Independent review targets current artifacts and the original release objective, not only implementation summaries. Record unsupported/unavailable environments without turning missing required functionality into a limitation.
|
||||
|
||||
## Definition of done
|
||||
|
||||
Every concrete requirement in the new objective and release coverage table has authoritative current evidence. Primary OA/SA engineering workflows work graphically, textually and through the API; help is navigable and useful; native Windows/Linux UI is intentionally designed and exercised; no blocking/high-impact defect remains; strict checks pass; independent findings are closed; durable docs accurately reflect the release; another engineer can build/run/test and use the supplied packages. V2 completion alone does not satisfy this goal.
|
||||
|
||||
## Historical milestone progress — hosted integration
|
||||
|
||||
Milestone4 desktop adapter is implemented on the existing Document/REST boundary.
|
||||
Windows native publish, graphical/text save, rejected invalid source, API artifact
|
||||
reload and stale-revision recovery are demonstrated. Offline/retry and exact local-copy/catalog Open native acceptance passed on Windows.
|
||||
Destination and catalog defects found afterward were fixed/reviewed/integrated; remaining platform/native cases retain their report scope. Constructor/keyed/order/Child/Incident/artifact and multi-source review, Home/recents and checkpoint recovery are now implemented. Their scoped native and automated results are in STATE.md; this older milestone is not a current absence list.
|
||||
|
||||
## Current execution refinement — 2026-09-07
|
||||
|
||||
First-release scope above is unchanged. The user-requested Windows development preview is packaged with565files, including all37 teaching-folder files, and is linked from `DEVELOPMENT_SUMMARY.md`. Latest Fit6 and two existing scroll/branch regressions pass; full desktop570/570, workspace bins, formatting and strict workspace/all-target Clippy pass. Corrected full server56/56 retains its unchanged-server scope. Exact build status and provenance are recorded in STATE.md. This is not an optimized final release.
|
||||
|
||||
Native6BAB operation/recovery/history passes and BCC1 corrected OA/SA nested routing passes retain their separate binary scopes. The latest DC50 preview launched with a clean PATH, and its four-card OA Fit overview was visible at1444×932 and1102×932. Minimum-height, pending-form and remaining process-purpose acceptance are still open. Computer Use was stopped by the user's Escape; leave both live GUI processes untouched and continue independent work.
|
||||
|
||||
Native Linux core436passed/2ignored is complete on its frozen455-file snapshot. The original server compile passed, unit2/2 and HTTP50/51 passed, and the single504-versus412 timing fixture failed twice. Its private preparation-gate replacement passes on Windows but still requires Linux execution. Preserve the original failures and exact final-state assertions; do not alter the production30-second deadline to make the fixture pass.
|
||||
|
||||
The fresh Linux GUI cross-build passed on immutable458-file snapshot `source7-20260907T102528493108Z`, including the Fit constructor fix. Run `20260907T1025486926611Z` completed in2m20s Cargo time with pre/post checks, ELF requirements and all37 lesson files verified; the compiler lease is released. The subsequent headless guest failed its early IO-APIC timer check on three bounded boots, before SSH/tests. Exact matching boot assets were extracted, but automatic approval review rejected the proposed transient boot workaround before launch. All task VMs are stopped; no application failure or current Linux server pass is inferred. Latest guide-label changes and the validated Exchange/CommonWork native oracles follow the frozen binary snapshot.
|
||||
|
||||
Next actions:
|
||||
|
||||
1. Retain all three guest boot records and exact-kernel proof; the proposed direct-boot action was denied by automatic approval review and must not be retried or split to bypass it. Resolve the runtime gate through an approved route or changed environment. Verify actual readiness and remaining monotonic budget before running the corrected Linux server gate. Do not restart on observation timeout alone.
|
||||
2. Preserve the reviewed guide corrections and validated expected-model/source Exchange/CommonWork oracles; the updated guide now builds on Windows and belongs in the next packaged/native snapshot. Complete native purpose authoring/history once control is available.
|
||||
3. Once native control is authorized again, complete current-layout minimum-height/Focus/pending-form checks, remaining engineering/hosted walkthroughs and actual Linux GUI acceptance using exact model/source/history evidence.
|
||||
4. Execute the corrected Linux server suite; complete final Windows/Linux optimized builds, package provenance, documentation/contracts, and fresh independent cross-disciplinary end-state review. Successful slices do not complete the goal.
|
||||
[Archived prior plans](history/2026-09-11-ytr83r-integration/README.md) preserve
|
||||
all previous milestone detail. Update this plan when a counterexample changes
|
||||
the right next action; do not lower completion criteria after substantial effort.
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# Current engineering work and evidence
|
||||
|
||||
Start with the [user goal](../PROJECT_GOAL.md), [current status](STATE.md),
|
||||
[execution plan](PLAN.md), [task ledger](TASK_LEDGER.md) and
|
||||
[decisions](DECISIONS.md). Follow [AGENTS.md](../AGENTS.md) and current
|
||||
[Gitea coordination](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/1)
|
||||
before changing shared work. Dated reports describe their own source, host and
|
||||
artifact; they are not current instructions or acceptance of later changes.
|
||||
|
||||
## Runnable milestones
|
||||
|
||||
- [Windows Studio and REST server authoring preview](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/releases/tag/v0.2.0-preview.authoring.20260911)
|
||||
- [Standalone Windows source CLI preview](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/releases/tag/cli-v0.2.0-preview.authoring.20260911)
|
||||
|
||||
These are unfinished development milestones. Their release notes and validation
|
||||
attachments define exactly what was shipped and tested. Full OA/SA engineering,
|
||||
analysis, maintained libraries and current Linux acceptance remain unfinished.
|
||||
|
||||
## Current local evidence
|
||||
|
||||
| Question | Authoritative record |
|
||||
| --- | --- |
|
||||
| Which source was integrated? | [Integration audit](release-integration-ytr83r-20260911.md), [CLI import review](release-cli-integration-ytr83r-20260911.md) |
|
||||
| What does lifetime rendering mean? | [Implementation and corrected review findings](release-lifecycle-render-ytr83r-20260911.md), [language specification](../docs/language.rst) |
|
||||
| What will native behavior be compared against? | [Independent fixture and procedure](fixtures/scenario-lifecycle-native/README.md), [exact pre-native evidence](experiments/lifecycle-native-ytr83r-20260911/) |
|
||||
| Which checks ran on this host? | [Host build record](release-host-bootstrap-ytr83r-20260911.md), [current status](STATE.md) |
|
||||
| What remains for Windows packaging? | [Current runtime/notice preflight](release-b4-package-preflight-ytr83r-20260911.md), [publication path](release-publication-path-ytr83r-20260911.md) |
|
||||
| Is Linux available? | [WSL outcomes and current user pause](release-wsl-postboot-ytr83r-20260911.md), [preserved offline inputs](release-linux-offline-preparation-ytr83r-20260911.md); setup/build/checking waits for the user's networking-resolution signal |
|
||||
| What changed for Linux delivery? | [Three-binary CLI packaging and checks](release-linux-cli-packaging-ytr83r-20260911.md), [portable REST restart probe](release-rest-probe-portability-ytr83r-20260911.md); native execution remains pending |
|
||||
| Which bounded follow-ups are proposals? | [Explicit Action allocation](release-qualified-action-design-ytr83r-20260911.md), [inheritance scaling diagnosis](release-inheritance-scaling-diagnosis-ytr83r-20260911.md) |
|
||||
|
||||
External staged-source, chooser and analysis/library owners maintain their current
|
||||
handoffs in Gitea and the [shared Wiki](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/wiki/Home).
|
||||
Import exact coherent source checkpoints before transferring conclusions to this checkout.
|
||||
|
||||
## History
|
||||
|
||||
[Archived integration ledgers](history/2026-09-11-ytr83r-integration/README.md)
|
||||
preserve source-specific facts and earlier ownership. Existing dated evidence and
|
||||
Git history remain available. Preserve unique findings and update links before
|
||||
moving or removing material; active issue #1 remains open for the unfinished goal.
|
||||
@@ -0,0 +1,64 @@
|
||||
# Resume after the user-requested reboot — 2026-09-11
|
||||
|
||||
Codex-ytr83r /root. The complete first-release goal remains active and unfinished.
|
||||
Read AGENTS.md, PROJECT_GOAL.md, current Gitea issues1/2 and Wiki before resuming.
|
||||
This is a pause for the user's reboot, not a completed milestone or failed test.
|
||||
|
||||
## Saved source and evidence
|
||||
|
||||
Branch: codex/release1-ytr83r. Production source is B4 lifetime rendering52f6e17
|
||||
plus CLI01b imported as023e3a8. b96286e records exact core/oracle evidence and
|
||||
current harness index. d041212 preserves owner instruction-sync history and the
|
||||
byte-exact supplied PROJECT_GOAL. a9a5768 records the Linux trial and contextual
|
||||
source integration review. Later checkpoint commits add this resumption note.
|
||||
|
||||
Core all-targets passed433 tests, one intentionally ignored probe; the optional
|
||||
probe separately passed. The independent lifecycle oracle and all six frozen
|
||||
baseline/rename files passed before any native GUI launch. See the harness index.
|
||||
No current desktop compile/test/native acceptance has been established.
|
||||
|
||||
## Interrupted Windows build
|
||||
|
||||
Cargo session93786 was interrupted at the user's request while compiling Iced
|
||||
dependencies (last log entry cc1.4.5). It exited1 from interruption; no final gate
|
||||
status JSON was emitted. A readback found no cargo.exe/rustc.exe process. Exact
|
||||
partial log and explicit interruption record are in
|
||||
experiments/lifecycle-native-ytr83r-20260911/. Preserve cached target outputs.
|
||||
|
||||
Use the existing ignored task-local toolchain, one Cargo job, after checking no
|
||||
other writer is active. From the repository root in PowerShell:
|
||||
|
||||
```powershell
|
||||
& '.local\run-gate.ps1' -GateName 'ytr83r-desktop-lifecycle-resume' -CargoArgs @('test','--locked','--offline','-p','oa-desktop','scenario_lifecycle','--','--test-threads=1')
|
||||
```
|
||||
|
||||
The wrapper loads .local/toolchain/env.ps1. Then run appropriate direct-canvas,
|
||||
full/strict desktop, server/CLI and GUI build gates serially. Review the new
|
||||
typography report: wide Unicode Create labels can overflow the fixed capsule.
|
||||
The proposed clipping correction is not implemented; preserve existing fixtures
|
||||
and define separate expected Unicode cases before native output. Actual Windows
|
||||
wide/minimum/Fit/Save/Undo/Redo/reopen acceptance and B4 packaging remain pending.
|
||||
|
||||
## Linux after reboot
|
||||
|
||||
Recheck wsl --status and wsl --list --verbose. Before reboot, no distribution was
|
||||
installed; WSL reported virtualization unavailable and Windows had a pending
|
||||
reboot. Hardware virtualization must be available (firmware or nested VM host).
|
||||
No host setting was changed and no reboot was performed by the agent.
|
||||
|
||||
Prefer a working WSL2 Ubuntu for real Linux build/runtime validation. The verified
|
||||
Ubuntu image and QEMU cache remain under .local/linux-host-ytr83r. Its bounded
|
||||
UEFI/TCG pilot ended without guest-ready evidence; all owned pilot processes
|
||||
stopped. Do not repeat failed delivery or infer Linux acceptance from the image.
|
||||
|
||||
## Coordination and next integration
|
||||
|
||||
Codex-9e8r6t owns contextual prerequisites; df6f5956 is available locally and has
|
||||
a reviewed minimal import path, with two B4-preserving merges. Later short-host
|
||||
layout and Save-procedure corrections are separate; inspect the current remote
|
||||
handoff before import. Codex-956atf owns chooser, shared constrained canvas design
|
||||
for issue2, and analysis/library contracts. Local audit agents have stopped their
|
||||
assigned work. They must recheck coordination before taking new tasks.
|
||||
|
||||
The two published Windows authoring/CLI prereleases remain available. No B4
|
||||
release should be published until actual current tests/native acceptance pass.
|
||||
+28
-149
@@ -1,170 +1,49 @@
|
||||
# Progress Ledger — first release
|
||||
# Integrated checkpoint — 2026-09-11
|
||||
|
||||
## Active continuation — 2026-09-10
|
||||
Codex-ytr83r, branch `codex/release1-ytr83r`. The full [user goal](../PROJECT_GOAL.md) remains incomplete. Live integration/acceptance work is in [Gitea issue5](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/5); the [project overview](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/1) and [process/scenario requirement](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/2) link other work. This file scopes integrated code and evidence.
|
||||
|
||||
The renewed user request is preserved in `PROJECT_GOAL.md`. Root is integrating
|
||||
the first release, prioritizing direct diagram authoring and behavior/scenario
|
||||
usability. The checkout began clean at `1797516` on `codex/release1-continuation`.
|
||||
Gitea issue #1 and Wiki Home were read; ownership and progress are coordinated there.
|
||||
## Verified code and capabilities
|
||||
|
||||
- Root owns integration, serialized builds, native Computer Use and these ledgers.
|
||||
`palette_repair` owns architecture authoring and `diagram.rs`; `behavior_audit`
|
||||
and `release_audit` independently inspect ergonomics and release coverage.
|
||||
- The recorded palette compilation errors have local corrections pending a fresh
|
||||
build. Earlier 584-test evidence does not cover those corrections.
|
||||
- This checkout began without the historical `.local` toolchain/cache/packages.
|
||||
Project-local Rust/Cargo 1.98.1 GNU/LLVM and verified LLVM-MinGW 20260908
|
||||
(clang 23.1.1) are installed. Locked dependencies are being fetched.
|
||||
Host: four logical processors, 8 GiB RAM; builds remain serialized.
|
||||
- Computer Use initialized successfully. The renewed request authorizes native
|
||||
evaluation; old session Escape holds and process IDs are historical. No Arcadia
|
||||
or Capella window is currently running.
|
||||
- WSL lists no installed distribution. Linux runtime/build acceptance remains an
|
||||
open gate; no historical denied guest workaround is being retried.
|
||||
Earlier published checkpoint `3ade397008cffe826d5f5a70e7c0233159e3bd42` contains production `29084050a7c020e0fb488be37c6e1ab235769cc0`; push and exact remote readback passed. It combines prior named behavior creation/history integration (`c81e285`, `e207b04`), corrected creation-oracle evidence (`b853a1e`), CLI-only integration (`023e3a8`) and B4 lifecycle rendering.
|
||||
|
||||
**Current branch is `codex/behavior-authoring-20260910`.** A distinct Gitea user
|
||||
`Codex-9e8r6t` is running another root/team on a separate environment with the old
|
||||
build cache. Our identity is **Codex-956atf**. Gitea #25 and #38 acknowledge the
|
||||
scope split: their palette/platform/package and subsequent scenario layout work;
|
||||
our process/scenario adapters and typed-history work. We will publish a clearly
|
||||
untested coherent snapshot on this distinct branch for their warm-cache checks.
|
||||
No shared branch-tip push or merge has occurred here.
|
||||
Their reports/results must not be claimed as local validation or overwritten.
|
||||
B4 renders local lifetimes, branch-local activation termination, neutral incomplete lanes, Create capsules and Delete markers with original occurrence identity. Real Windows OA/SA six-case matrices and separate name-only Save/Undo/Redo/reopen workflows passed complete independent Project, reachable-source and physical-file comparisons. These are bounded fixture checks, not complete product usability acceptance.
|
||||
|
||||
Implemented locally, awaiting compilation:
|
||||
- [Native lifecycle observations and limits](release-lifecycle-native-ytr83r-20260911.md).
|
||||
- [OA evidence archive](lifecycle-native-ui-oa-archive-20260911.md) and [SA evidence archive](lifecycle-native-ui-sa-archive-20260911.md).
|
||||
- [Lifecycle implementation and independent findings](release-lifecycle-render-ytr83r-20260911.md).
|
||||
- [Creation and source integration evidence](release-staged-source-integration-ytr83r-20260911.md).
|
||||
|
||||
- Palette compile repair, retained inline name after rejected stale Submit,
|
||||
nonfinite click rejection and rename/allocation/history regressions.
|
||||
- One interactive process renderer with presentation-only inferred ordered
|
||||
paths; first editing batch preserves adjacency atomically. Independent source
|
||||
review found no confirmed defect. Root added 3+ step/before-first insertion
|
||||
and incompatible imported conversion tests after the reviewer hit model capacity.
|
||||
- Scenario Action/State/Reuse/empty Fragment tools capture clicked operand/slot
|
||||
and optional lifeline. Pointer-to-App, recovery, stale-event and history tests
|
||||
are written. A bounded exact caller/use return path and scoped mounted-scroll
|
||||
restoration are implemented with regressions, also untested.
|
||||
- Advanced typed stored-history editor uses existing OpenAPI type descriptors;
|
||||
twelve focused tests cover typed selectors, historical IDs, invalid raw input,
|
||||
recovery and source/history. Core validation remains authoritative.
|
||||
- Normative catalogs now include ExchangeCategory, ContextualLink and both
|
||||
Exchange trace pairs. Manual trace help and scope source version corrected;
|
||||
strict Docutils 0.22.2 HTML regeneration passed.
|
||||
Source `321897fd55af9e07afa2c6ebf1e50be49e86f7d9` integrates the published contextual source-creation/minimum-card corrections from b2c3e540 with B4 and the header clipping correction. Its own Windows desktop all-targets gate passed 704 tests. [Exact selective integration, independent union review and Windows results](release-contextual-integration-update-ytr83r-20260911.md).
|
||||
|
||||
Reference evidence: actual Capella 7.1 scenario/lifeline/LOOP creation and saved
|
||||
XML are recorded in `release-capella-scenario-benchmark-20260910.md`; the isolated
|
||||
Capella process closed normally and is absent. This is not Studio acceptance.
|
||||
Published production `865483b4c66361a01fe0d9e731859220bafb4136` adds the reviewed child-work correction: first refinement transfers existing Allocation and RoleAssignment identities, metadata and artifact references to the child; later siblings inherit only a unique performer. OA/SA regressions cover nested children, roles, folded grouping, unallocated/mixed controls and exact history. The extracted Studio `BF9C2E61…` passed actual OA first-child, sibling and grandchild creation plus Save/Undo/Redo comparisons against original complete model/source checkpoints. Reopening and the SA native counterpart remain unverified. [Child-work fixture and oracle](fixtures/child-work-native/README.md); [native OA evidence](experiments/native-child-oa-865483b-20260911/README.md); [live acceptance boundary](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/4).
|
||||
|
||||
Toolchain bootstrap exec session **20812** completed successfully: verified
|
||||
LLVM-MinGW 20260908 ZIP SHA256
|
||||
`1bcf74d06b724aeecaa6412ca85f5b26fb1da770e7cdcefa9263c9c5c3ad34b6`, extracted
|
||||
successfully; stable **1.98.1** GNU/LLVM, rustfmt and Clippy installed.
|
||||
`.local/toolchain/env.ps1` configures task-local Cargo/Rustup/PATH and one build
|
||||
job. Locked dependency fetch is local exec session **13108**. No build/test pass
|
||||
is established; the target cache is still cold.
|
||||
## Verification scope
|
||||
|
||||
The coherent candidate `01bb52a` passed `cargo fmt --all` and `git diff --check`.
|
||||
Direct push failed because this fresh host has no Git credentials. Authenticated
|
||||
Gitea MCP transferred its exact verified Git bundle through
|
||||
`codex/behavior-transfer-20260910` (`harness/transfers/behavior-01bb52a.bundle.b64`),
|
||||
prerequisite `1797516`, decoded SHA256
|
||||
`85A38B00207036DCB8DB24ED4A7E80B2BE670275EEB7B00413D36153322ADA5D`.
|
||||
This **UNTESTED integration snapshot** lets the other host use its warm cache;
|
||||
transfer is not acceptance. Their broader architecture
|
||||
palette/Escape repair should be reconciled with our overlapping compile/stale
|
||||
repairs while retaining the additional invariant tests. Their measured minimum
|
||||
scenario canvas is too small; they own the subsequent layout implementation.
|
||||
Their isolated palette commit is `7d1df4f`, fetched locally for reconciliation.
|
||||
On this Windows host, core all-targets passed433 tests with one intentionally ignored scaling probe; that probe separately passed with unoptimized timings. Production29084050 passed focused lifecycle22 and full desktop675 tests (the22 are included in675), and the ordinary GUI build/launch. An equivalent test-helper let-chain correction subsequently passed strict workspace/all-targets Clippy and its focused regression1/1. These results predate the newly identified header-rendering correction.
|
||||
|
||||
Independent F1 review found a real optional-value cache collision in `01bb52a`.
|
||||
The follow-up distinguishes null/scalar/list/tagged constructor identities and
|
||||
adds actual inner selector-event tests. Independent source recheck found no
|
||||
remaining collision; execution is still pending. The original full-platform
|
||||
fetch was stopped after network timeouts; current Windows desktop test build
|
||||
is exec session **51036**, compiling dependencies. Root owns this Cargo lease.
|
||||
- [Exact current-host logs and inputs](experiments/lifecycle-native-ytr83r-20260911/).
|
||||
- [Test-only Clippy correction](lifecycle-clippy-correction-ytr83r-20260911.md).
|
||||
- [Current-host toolchain setup](release-host-bootstrap-ytr83r-20260911.md).
|
||||
- [Independent Windows gap review](release-windows-gap-review-ytr83r-20260911.md).
|
||||
|
||||
Next: focused `architecture_authoring_tests`,
|
||||
`behavior::topology`, `direct_clicked`, caller-navigation and `history_fields::tests`,
|
||||
then full relevant gates and a fresh native binary. Root owns all Cargo on this
|
||||
host. Worker output or tests existing in source are not passes.
|
||||
The closed 321897f Windows gate passed 704 desktop tests with zero failed/ignored/filtered; all 261 recorded build inputs remained unchanged. Matching server `ABA87A5F…` passed semantic HTTP checks (204 schemas, 19 values, 9 rejected invalid shapes) and the corrected REST probe's 25 requests. [Closed runtime evidence](experiments/windows-runtime-321897f-20260911/README.md) preserves both earlier source-version failures. [Earlier integration checkpoint evidence](experiments/contextual-lifecycle-integration-ytr83r-20260911/README.md) preserves server/CLI 68 across 7 targets, schema 72/204 after the missing-jsonschema correction, and the old GUI's wide-name passive Save comparison.
|
||||
|
||||
Gitea Actions run16 is queued for both ubuntu-latest and windows-latest with no
|
||||
started steps, as are older listed runs. No usable current Linux runner result
|
||||
is established and no duplicate dispatch was made. Progress is concrete;
|
||||
release acceptance remains incomplete. Older checkpoint paragraphs below retain
|
||||
their original revision scope.
|
||||
The later 865483b Windows desktop gate passed **707 tests**, including the three focused child-work regressions, with zero failed/ignored/filtered. All 261 recorded build inputs stayed unchanged. The matching debug GUI build and strict **desktop-only** all-targets Clippy passed; child-work and staged-source checker compilation/admission also passed. [Closed gates and exact inputs](experiments/windows-gates-865483b-20260911/README.md). These checks do not establish the remaining native rendering or packaged acceptance.
|
||||
|
||||
**Checkpoint publication — 2026-09-10:** the user requested committing all current changes and pushing `codex/release1-continuation` to both `origin` and `gitea`. This is a work-in-progress snapshot. The interrupted palette-focused compilation failed with E0659 (ambiguous `column!` in `app/architecture_authoring/palette.rs:168`) and E0507 (moving the view out of an Arc in `diagram.rs:624`); an unnecessary-pattern-parentheses warning also remains in `architecture_authoring.rs:346`. The prior 584-test pass does not cover this newer palette revision. No Cargo/rustc process or local subagent was active at the publication preflight. Fix these errors and complete automated/native acceptance before packaging the redesign. Gitea coordination starts at issue #1 and the project Wiki; older evidence below retains its original scope.
|
||||
The extracted package's unchanged server `ABA87A5F…` passed the actual HTTP schema checks (204/19/9) and 25-request REST editing/persistence/restart probe with a restricted process PATH and unrelated working directory. Seven raw response pairs and exact source snapshots matched; all five test-owned server processes stopped. The first REST run's PowerShell-added PATH directory is preserved as historical evidence; the corrected restricted-PATH rerun is authoritative. [Packaged server evidence](experiments/packaged-server-865483b-20260911/README.md). This completes bounded server runtime acceptance, while the new Windows preview remains unpublished pending the separate GUI checks.
|
||||
|
||||
**Latest priority checkpoint — Capella consultation and redesign:** the user rejected the large creation buttons and explicitly requested consulting the installed Capella. Native control resumed for that consultation in a disposable Capella 7.1 workspace. Actual palette, F2 rename and activity allocation were exercised; saved XML confirms allocation distinct from ownership. The horizontal creation/connection rows are being replaced with a compact expandable palette and inline naming. This supersedes the Escape hold and toolbar-fix proposal below. New implementation, native acceptance and a fresh user build remain pending. [Observed benchmark, alternatives and acceptance criteria](release-capella-basic-editing-20260907.md). The first-release goal remains active.
|
||||
## Material limitations
|
||||
|
||||
**Latest checkpoint:** basic-editing v1 passed full desktop584/584, Windows binaries, format and strict all-target Clippy. Native v1 D8CFAA confirmed direct contextual named Entity creation at100%, and found a22px placement shift caused by the pending toolbar changing canvas height. The user pressed Escape at the attempted Undo; that action did not execute. **Desktop control is paused again.** Process9600 remains the isolated unsaved QA application; do not overwrite its binary or presume a clean close. Code-only placement fix is continuing. [Exact v1 evidence and remaining acceptance](release-basic-editing-reset-20260907.md). This supersedes the resumed-control state below; the first-release goal remains incomplete.
|
||||
Native wide Latin/CJK checks found neutral lifeline header text overflowing its card; Create labels remained contained. The [header correction](lifecycle-header-overflow-correction-ytr83r-20260911.md) is included in the tested 865483b source, but matching corrected pixels and combining/emoji native appearance remain unverified. The old GUI's wide-name passive Save pass establishes identity/model/source/file equality, not corrected rendering. Complete combined native and packaged acceptance remain separate. During the OA reopen check, the Computer helper repeatedly failed with `window capture timed out: timed out waiting on channel`; fresh window selection and one clean helper restart did not recover capture. Native input stopped at the Open dialog with the accepted grandchild result already saved. This is a validation-tool failure, not evidence of a model failure or a successful reopen.
|
||||
|
||||
**Superseding priority checkpoint:** the user explicitly said “Resume desktop checks” and rejected the focus on advanced review before basic diagram editing. Isolated native checks resumed and reproduced the global creation catalog, placeholder creation/camera jump and noisy review. Contextual named creation and compatible layer-transition destinations are now being implemented and tested. Their new executable has not yet been accepted. [Current priority and scoped evidence](release-basic-editing-reset-20260907.md). The older Escape hold and next-step priorities below are superseded; historical binary/test results retain their original scope.
|
||||
Other chooser/canvas and text-navigation contributions still require their own integration and verification. Calculations, quantities/units, maintained libraries, practical large-model textual workflows, complete OA↔SA engineering parity, documentation and fresh cross-disciplinary review remain full-goal requirements. See the [analysis proposal](release-analysis-semantics-design-20260910.md), [library proposal](release-libraries-semantics-design-20260910.md), [text-authoring design](release-textual-authoring-design-20260911.md) and [decisions](DECISIONS.md) for attributed design evidence, not completed implementation claims.
|
||||
|
||||
Current checkpoint: 2026-09-07. The requested Windows development preview is built and packaged with its runtime, documentation, notices and complete examples. Latest Fit6, two existing scroll/branch regressions, full desktop570/570, workspace bins, formatting and strict workspace/all-target Clippy pass. Corrected server56/56 retains its earlier unchanged-server scope. Packaged DC50 GUI launched and the OA graph Fit overview was observed at1444×932 and1102×932; minimum-height and pending-form native acceptance remain open. The user stopped Computer Use with Escape; do not resume native control without renewed authorization. Native Linux core436passed/2ignored remains verified; corrected Linux server and full Linux GUI build/runtime remain open. The fresh Linux desktop cross-build passed on Rust1.90 in2m20s using the preserved cache; ELF and all37 teaching files are verified. Three later guest boots failed the early IO-APIC timer check before SSH/tests; the exact-kernel workaround was then rejected by automatic approval review before launch. All task VM processes are stopped. **Goal active; release incomplete.** The authoritative objective is `C:\Users\User\.codex\attachments\10b0f701-1743-4de7-9ee1-225920e19649\goal-objective.md`: a usable, feature-complete OA/SA engineering tool for Windows and Linux with graphical/source/REST parity, artifact traceability, offline guidance and independent end-state acceptance.
|
||||
## Platform and published deliverables
|
||||
|
||||
## Workspace and ownership
|
||||
**Linux setup, downloads, builds and checks are paused by explicit user instruction until the user reports networking resolved and instructs resumption.** Ubuntu26.04/WSL2 shell execution was observed; guest networking remained unavailable. Offline preparation was a workaround, not a network fix or Linux acceptance. Host VPN settings were not inspected or changed. [WSL evidence](release-wsl-postboot-ytr83r-20260911.md) and [preserved offline inputs](release-linux-offline-preparation-ytr83r-20260911.md) retain exact outcomes.
|
||||
|
||||
- Current branch `codex/release1-continuation`, checkpoint **4b6b5fc1977523e8cf52fdd5503a0a71c87a3f63** commits290files of source6 work and evidence. [Bounded checkpoint audit](release-checkpoint-integration-review-20260906.md) verified65pinned manifest entries and145exact staged/working fixture/archive files; `.gitattributes` protects their measured line endings. Integrated ancestry starts at `c091f80`; [branch audit](release-branch-integration-20260906.md) prevents reimplementing already integrated work. New source7 implementation is authorized but not yet accepted.
|
||||
- Historical6BAB native scope (superseded in rendering by BCC1 and Fit by DC50): acceptance binary `.local/native-qa/bin-source7-acceptance/arcadia-studio.exe` SHA256 `6BAB6CDE7D31326F3C3CF7342D9B25FB6ADA032864BBE8104625E5EC9736283B` follows the Windows433/55/561 and strict/build gates below. Reset and ReadStatus each passed exact complete model/source Save/Undo/Redo and reopening with the correct derived Reply allocation30/31. OA caller→First.B passed exact persistence/history/reopen; routing remains open. Process11060 closed cleanly; isolated old6 recovery process11240 also closed after its complete source/JSON/raw-draft pass. Old5 process5244 subsequently passed exact30-source/full-JSON recovery and raw-workspace comparison. Current7 process8224→10420 passed invalid nested-link draft crash recovery; completing the recovered SA X→[R3,Inner]/B link passed exact model/two-source Save/Undo/Redo and saved reopening. Those historical native processes closed; the later BCC1 routing and DC50 Fit evidence is recorded separately below. [Native evidence](release-source7-native-acceptance-20260907.md).
|
||||
- Completed bounded90minute Linux gate: one-job Rust1.90,455-file immutable snapshot SHA256 `7104946c89b2be7a1a030fdff47cb4405bacf05357d68cf35d181e860f55c279`, launched07:02:43UTC, deadline08:32:43UTC. Server compilation passed17m10s; unit2/2 and HTTP50/51 passed, with one504-vs-412 test assertion reproduced in an isolated rerun. Its two-second read/PATCH checks passed, but later persisted-state assertions were not reached. This establishes a timing-dependent fixture/deadline mismatch, not a production locking defect. Native core completed436passed/0failed/2ignored across40targets, Rust1.90, compile23m03 and1992seconds total; all455 source pre/post checks passed. The two ignored cases are optional inheritance scaling and explicit strace fault injection (earlier separately scoped injection passes remain historical). All artifacts were copied before sync/poweroff; QEMU6280, launcher5000 and seed816 were absent08:13:56UTC. [Current Linux report](release-source7-linux-gate-20260907.md). Earlier Linux evidence retains its older snapshot scope. WSL remains pending restart; no host restart is authorized.
|
||||
- Windows session5735 completed all six gates with exit0: focused prepared-HTTP and cancellation, full server56/56 in68.78s, format10.44s, strict workspace/all-target Clippy99.60s, bins102.22s. Status/logs: `.local/source7-post-routing-gates-status.json`. Distinct GUI BCC1FC6DB68123C9D3A43CE53AA8EF59E33C54080632C104C99ED13270D5333F process8568 passed the scoped corrected routing checks and closed cleanly. [HTTP evaluator correction](release-http-preparation-phase-regression-20260907.md), [routing correction](release-contextual-route-native-fix-20260907.md), [native details](release-source7-native-acceptance-20260907.md). These passes precede the now-tested [Fit/layout correction](release-process-fit-layout-audit-20260907.md).
|
||||
- The requested Windows preview is `dist/ArcadiaStudio-preview-source7-20260907-x86_64-pc-windows-gnullvm/arcadia-studio.exe`; its565-file ZIP includes all37 teaching-folder files and has SHA256 `1fac10b30daf2f8e1dac29e9469485e4bea142e77ff5a13e4c9d1e41ff03abb2`. The adjacent provenance records actual source/binary hashes. This is a development profile, not an optimized final release. Latest gates are in `.local/source7-latest-preview-status.json`; full desktop570/570 completed in198.82s test time,200.63s command time, exit0 in `.local/source7-latest-full-desktop-status.json`. Initial Fit4/2 failures exposed real zero-size-slot remounting; corrected constructors pass unchanged expectations. [Fit review](release-process-fit-independent-review-20260907.md), [development summary](../DEVELOPMENT_SUMMARY.md).
|
||||
- Computer Use is stopped. Read-only inspection initially found preview9148 and second GUI8044 live; later9148 exited without agent intervention, while8044 remained untouched. No GUI control/closure is authorized for background verification. The cross-compiler lease ended after the successful142.766s monotonic run; no cross processes remain. The Linux worker completed a bounded headless guest experiment and released the lease. Three boots (same configuration twice, then Normal boot priority) reached terminal IO-APIC timer panic before SSH/Cargo; all task VM trees stopped and exact evidence was preserved. Exact matching kernel/microcode extraction passed without changing base/overlay hashes; automatic approval review rejected preparation/launch of the transient no_timer_check experiment as “blocked by policy.” No fourth boot or corrected tests ran. No task VM is live; see [the preserved boot evidence](release-source7-linux-server-corrected-gate-20260907.md). [Cross-build report](release-source7-linux-gui-cross-build-20260907.md).
|
||||
Peer Codex-9e8r6t published the separately verified [Windows contextual prerelease](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/releases/tag/v0.2.0-preview.contextual.20260911), source `b2c3e540`; [its publication handoff](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/2#issuecomment-406) binds its artifacts and original-host checks. It is not a B4 release from this branch.
|
||||
|
||||
## Current source build and stopped runtime gate
|
||||
## Record navigation
|
||||
|
||||
The guide-only followup compiled successfully with locked Windows workspace bins in12.70s Cargo/12.82s command, exit0. Status `.local/source7-guide-current-build-status.json` records guide SHA256 `80A6797051FB28E767F2AB5DD2F335ABCD9F3E5F3A75C731CDE46D8B90D5C92E`; resulting GUI SHA256 `327FBDF734716A7F1AF2874183D46A531A8F429BA1A478E83A3E7F2EB1D6248B`. Formatting and independent actual-label review pass. This later debug artifact does not replace the user-facing packaged DC50 preview, its archive or earlier native evidence. No full570 rerun is claimed for the text-only followup.
|
||||
|
||||
Previous goal turn made progress by confirming full570 tests and handing off the package/summary. This continuation produced the successful Linux GUI build/ELF evidence, validated purpose oracles, corrected/built guide text, and concrete guest-boot evidence. Goal remains active/incomplete. The denied direct-boot action must not be split or retried to bypass automatic review; no new Linux job is running or scheduled.
|
||||
|
||||
## Latest Linux build and purpose-oracle evidence
|
||||
|
||||
Linux GUI snapshot458files `source7-20260907T102528493108Z` has archive SHA256 `9b10aa8457a5371367f7e24a0b2260b63c19fed69873d98a3972062a744503d7`. Run `20260907T1025486926611Z` passed locked/offline Rust1.90 zigbuild, both source checks and independent ELF/package readback. GUI72,953,296bytes SHA256 `330A3872D48EBF24AD21E6D4FA13CB6098BEBB3CB7FF82A2E082925CCF3DD346`; ELF64PIE, glibc requirements≤2.28, all37 lesson files exact. Actual Linux GUI runtime is still unverified. Subsequent guide-label text is excluded from both published Windows and this frozen Linux preview.
|
||||
|
||||
Extended native checker `source7-native-contract-incidence.exe` SHA256 `16994DC437F5CCC5E5F6FBB4212C03449F2389AE4615FA98B2930B3637468EE3` compiled with warnings denied in4.05s during a coordinated short lease. OA Exchange and SA CommonWork self-checks pass: exact contextual assembly, unchanged precedence, wrong direction/work rejection, caller-only exchange coverage and stable source update. Separate43-element/10-relation incidence fixture baseline passes; all original golden hashes and preserved OA-link/Reset accepted cases remain exact. Independent review found no material issue. [Cases and reproducible native steps](fixtures/source7-native/README.md). Native purpose authoring/history remains pending.
|
||||
|
||||
## Completed and verified at this checkpoint
|
||||
|
||||
| Scope | Evidence and limit |
|
||||
|---|---|
|
||||
| Historical source6 diagnostics and guide | Semantic filter **108 desktop + 2 core** passed; full desktop **527/527** passed; strict all-target Clippy and GUI/server build passed (`.local/semantic-diagnostics-final-tests.log`, `desktop-diagnostics-final-tests.log`, `diagnostics-final-clippy.log`, `diagnostics-final-build.log`). Subsequent guide **3/3** focused tests, strict Clippy and build passed (`.local/guide-navigation-{tests,clippy,build}.log`). The guide test increases compiled desktop inventory to528; no full528 run or final full-workspace total is claimed. |
|
||||
| Current artifact implementation checkpoint | Full desktop **518/518**, corrected fresh artifact picker **6/6**, strict all-target Clippy passed. Logs `.local/source6-artifact-inspector-desktop-tests2.log`, `.local/source6-artifacts-final-tests2.log`, `.local/source6-artifacts-final-clippy3.log`. Four subsequent artifact recovery falsifiers and two core absence oracles passed separately. This predates latest display corrections. |
|
||||
| Native artifact MOVE / CREATE / DELETE | All three complete source Apply/Save, one Undo/Save and one Redo/Save oracles passed; **nine saves succeeded first attempt**. MOVE invalid Owner and disabled optional text, and CREATE excluded row/all eight decisions/reserved UUID survived controlled-stop recovery exactly. DELETE removed destination from identified moved-source absence while retaining the source link. [Exact native report](release-artifacts-native-20260906.md), [fixture/generator instructions](fixtures/release1-native/artifacts/README.md). |
|
||||
| Source-6 atomic batch review | Included valid and excluded invalid raw rows survived forced-stop recovery; repaired two-source Apply and complete Save/Undo/Redo source comparisons passed. [Native report](release-current-native-verification.md). |
|
||||
| Scenario ordered-member variant | At1100×700, Take rejected ambiguous insertion; explicit final order retained the local self-test. Complete Apply/Save, Undo/Save and Redo/Save matched the independent source oracles with only the schema5→6 header token upgrade. All three saves succeeded first attempt. Branch steps/fragment branches and this variant's crash recovery remain separate cases. [Exact native report](release-current-native-verification.md). |
|
||||
| Native guide navigation | On current guide binary at1100×700: related Scenarios-bottom→Exchanges opens the new heading; filtering `exchange` retains the current topic's bottom position; `Git` auto-selects Build first and opens its heading. No dirty model. Root's actual native observations close these bounded scroll cases; [implementation/tests](release-semantic-conflict-diagnostics-20260906.md) remain separately scoped. |
|
||||
| Communication means | Drawing/create/reuse and legacy reverse full-model/source/history passes are retained. Corrected reverse Apply now opens the explicitly requested OA scenario in Behavior, preserving the original four-object Architecture. Distinct oracle predicts10UUIDs/zero placements; accepted model/all3sources, Undo baseline, Redo and reopen pass. Every Save succeeded first submission. Navigation5focused tests, strict Clippy and bins build pass; compiled desktop535 is not a full535 run. [Exact report](release-communication-native-20260906.md), [implementation](release-behavior-transition-context-20260906.md). |
|
||||
| Teaching project and Home hook | Corrected94element/62relation/5view/68trace/30source example passes4expanded direct-rustc tests. Minimum-size Home/guide, raw-source guard, reviewed NameTake and exact source/history/reopen pass. Navigation fix is reverified. Additional data/interface/type drill-down, saved-view comparisons, invalid-view guard and typed Entry action selection pass. Fold/unfold and function rename/Undo/Redo each match complete model/all30source oracles, five first-submission saves; saved reopen retains the Entry target identity. Complete process/scenario/reuse/evidence lesson remains open. [Native report](release-teaching-native-20260906.md). |
|
||||
| Child/Incident and source-5 recovery | Two-row attachment recovery and complete Apply/Save/Undo/Redo passed, every save first attempt. Exact old source-5 raw/base/include/path retention, manual repair and two-file Save passed. [Attachments](release-attachments-native-20260906.md), [source-5 native](release-source5-native-recovery-20260906.md), [independent review](release-source5-checkpoint-independent-review-20260906.md). |
|
||||
| Dense saved views | Forty-label baseline and selected real relation identities inspected; filter, duplicate/hide, original10/copy9 persistence across restart and exact model oracle passed. Additional coverage/context intersections, empty selections and Names-and-items preview/Apply/Undo observed. Minimum-size overview requires working zoom/pan. Limits remain in [native evidence](release-current-native-verification.md). |
|
||||
| Core/source/REST integration | Fresh `--all-targets` core run: **433 passed/1 ignored**,44 target summaries, exit0 in138.72s; fresh server run: **55/55**,4 summaries, exit0 in64.74s. Logs `.local/source7-core-all-targets.log`, `.local/source7-server-all-targets.log`; exits/timings in `.local/source7-current-gates-status.json`. These supersede the earlier combined core count and pre-budget-fix HTTP51 checkpoint. OpenAPI **72fixtures/204schemas/90leaves** and both strict reST renders pass. [Core checkpoint](release-source7-core-checkpoint-20260906.md), [API integration](release-source7-api-integration-20260906.md). |
|
||||
| Source7 desktop authoring and recovery | Latest routing3/3 and full desktop **564/564 pass**; [routing checkpoint](release-contextual-route-native-fix-20260907.md) records exact logs. Earlier full desktop **561/561 passed**, zero failed/ignored,263.39s in `.local/source7-desktop-full-tests2.log`. The initial560/1 run exposed canvas scroll-state loss when a new conditional navigation control shifted its widget; a fixed navigation container corrected it, and the unchanged regression passed. Coverage includes qualified5, operation3 (selected Async request fields), semantic source7 facets/omission3, recovery8, legacy source5 13, artifact search2 and focus guard1. Exact old-version enum/cache-path intent rejection and raw text retention are covered. The complete-scope endpoint picker preserves access beyond512; canvas preview remains bounded. Guide now accurately describes attachment/artifact reconciliation and artifact filtering. [Desktop checkpoint](release-source7-desktop-authoring-20260906.md), [recovery](release-source7-recovery-20260906.md). |
|
||||
| Windows strict/build checkpoint and native7 | Strict workspace/all-target Clippy passed2m50s; root observed final format exit0/empty log. Three affected semantic tests passed3.46s after equivalent lint edits; native build exit0 recorded in `.local/source7-current-gates-status.json`. These earlier results scope the6BAB binary, not the later routing test binary or pending new strict/build sequence. Reset/ReadStatus full model/all-source Save/Undo/Redo/reopen pass. OA First.B persistence passes but routing acceptance remains open. Old6 Recover preserved the complete30-file bundle/workspace/Ports DTO, exact spaces/Unicode/fingerprint/reserved identity; Cancel/Save as and JSON export passed full independent source/model equality. Original seed unchanged. Old5 complete accepted/raw recovery, exact repair/30-source Save and full-model export pass. Current7 invalid nested draft crash recovery plus completed link Save/Undo/Redo/reopen pass; rendering still uses the older binary. [Native report](release-source7-native-acceptance-20260907.md). |
|
||||
| Current Linux source7 and cross-built probe | Frozen native server compilation passed17m10s; unit2/2 and HTTP50/51, with one timing-dependent504-vs-412 assertion reproduced twice. No complete server pass; later targets not claimed. Native core436passed/0failed/2ignored; VM cleanly off08:13:56UTC. Separately, the complete Windows macro/build-script cross-build probe's hash-verified ELF executed successfully on Linux; this is neither a full desktop build nor GUI acceptance. [Native Linux gate](release-source7-linux-gate-20260907.md), [cross-build probe](release-linux-cross-build-probe-20260906.md). |
|
||||
| Historical Linux/MSRV core and server | Ubuntu24.04.4/x86_64/glibc2.39/ext4/Rust1.90: core38/38, patched journal4focused/42full and3explicit syscall fault cases pass on recorded snapshots. Actual server build passes34m42s including3m24s pause. Real HTTP modular import, invalid PUT unchanged source/ETag, clean SIGTERM and exact model/source/ETag restart pass on the older28-source lesson snapshot. Full server test command timed out while compiling; zero tests executed. That gate shut down cleanly21:53:56; the separate current gate has the scoped results above. Current desktop/Linux package unverified. [Linux report](release-linux-validation-options.md), [journal contract](release-linux-journal-publication.md). |
|
||||
| Packaging foundation | Portable Windows QA staging, dependency/runtime/font/Rust notices, archive readback and clean-PATH smoke passed at earlier binaries. [Package report](release-windows-package-review.md). QA archives are not current final release artifacts. |
|
||||
|
||||
Historical full platform/native milestones retain their original revision scope; [complete prior ledger](history/2026-09-06-ledgers-before-consolidation/STATE.md) preserves all evidence links and previous environments. A test or native pass never transfers automatically to a changed executable.
|
||||
|
||||
## Active findings and checks
|
||||
|
||||
Native artifact review exposed same-row conflict wording and reserved identities shown as deleted. Corrections retain typed actual-slot equality, row-addressed reservations and accepted-identity precedence; independent re-review found no further material defect. The completed108-semantic/527-desktop checks supersede the earlier interrupted run and include reserved-label/partial-creation regressions. Revised artifact wording still needs its own native observation on the current binary. Guide-scroll implementation/tests/build and the bounded native cases above passed. [Diagnostic/guide report](release-semantic-conflict-diagnostics-20260906.md).
|
||||
|
||||
**F1** teaching onboarding/review/history has current-binary native evidence; remaining tutorial work is scoped in its report. **F2**, faithful behavior-canvas SVG export, is deferred by root's scope decision: export remains architecture-only and must be described that way; no faithful scenario/process/state SVG acceptance is claimed.
|
||||
|
||||
Reverse scenario's first-dependency focus/overlapping architecture placements and teaching NameTake's separate layer/view mismatch are fixed and natively reverified. Latest NameTake4focused tests, strict alltargetClippy28.26s and binsbuild50.50s pass; no full539desktop pass is claimed. [Navigation evidence](release-semantic-review-navigation-20260906.md). Minimum-size process working space is cramped; native lesson evidence records23% overview versus100% scrolling rather than claiming readability at Fit.
|
||||
|
||||
Independent scope reconciliation verified **S1** missing contextual assembly into repeated/nested process references and **S2** missing maintained interface-operation identity in parameterless messages. Isolated existing-core probes falsify the proposed workarounds. Root ratified the jointly critiqued [source7 integration decision](release-source7-integration-decision-20260906.md), including deterministic cache-independent admission, whole-link deletion, purpose-specific analysis, exact Call-derived operation/Reply identity and one frozen old5/6 checkpoint migration. These contracts now have implemented core/source/REST/desktop/recovery paths and the current automated gates above; remaining native7 and current Linux acceptance remain incomplete. [Scope reconciliation](release-scope-reconciliation-20260906.md).
|
||||
|
||||
Linux ordinary rename's empty-target replacement was independently reproduced and corrected using `renameat2(RENAME_NOREPLACE)` without fallback. Exact errno/no-write assertions now pass under actual syscall injection. This establishes the ext4 publication invariant, not power-loss durability, hostile ancestor protection or model-file compare-and-swap. Windows retains its no-replace API and bounded scanner retries.
|
||||
|
||||
## Remaining gates and next actions
|
||||
|
||||
1. Fit implementation/review and automated gates are complete, including full570 desktop tests. Finish its native minimum-height/Focus/pending-form checks when native control resumes. Preserve completed BCC1 routing and old5/old6/current7 recovery/source/history oracles. Complete remaining current-binary S1/S2 purpose workflows and teaching cases; operation allocation deletion remains subject to independent channel validation. Native-check revised reserved labels and same-row conflicts. The later guide-only control-label correction passed formatting, independent review and the fresh workspace-binary build; it awaits the next packaged/native release snapshot.
|
||||
2. Close uncovered selective-view lifecycle cases; advanced ordered/process/scenario/fragment variants beyond the accepted Scenario case; typed state actions and pending Linux data/hosted/compact workflows. Implemented semantics must not be called missing merely because acceptance is outstanding; [coverage audit](release-current-coverage-20260906.md) is the baseline map, newer scoped reports supersede its old gap claims.
|
||||
3. Verify complete current Linux integration suites and desktop/server build/runtime, then supported-size native workflows. The successful core/MSRV pilot is a reusable starting point, not application acceptance.
|
||||
4. Preserve current strict Clippy/format/reST/example/source/OpenAPI results and rerun affected checks after changes. Complete optimized/MSRV/final platform gates, actual hosted transport variants and final migration/package checks. Rebuild portable distributions from the accepted exact tree, validate notices/runtime/content/checksums and clean-environment launch before replacing old bundles.
|
||||
5. Conduct fresh independent requirements/domain/parity/architecture/runtime/UX/end-state reviews; resolve material findings. Commit/integrate authorized source with reviewable scope; remote publication is not requested. Only complete the goal when its applicable criteria are met.
|
||||
|
||||
Meaningful progress continues: native lifecycle and Linux no-replace uncertainty have been resolved with exact oracles. The highest-value work is current-binary validation and closing uncovered engineering workflows, not accumulating repeated historical headings or treating aggregate test counts as release completion.
|
||||
The [one-time record migration](history/2026-09-11-ytr83r-record-migration/README.md) preserves the exact prior STATE ledger and marks PLAN/TASK_LEDGER superseded. [Earlier integration history](history/2026-09-11-ytr83r-integration/README.md) preserves source-attributed older results. Historical leases, failed approaches and next-step wording do not override current Gitea coordination or the Linux pause.
|
||||
|
||||
+126
-77
@@ -1,96 +1,145 @@
|
||||
> Superseded on 2026-09-11. This document is a preserved historical checkpoint.
|
||||
> Live plans, priorities, ownership and handoffs now reside in [Gitea issue 1](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/1#issuecomment-410) and [issue 2](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/2).
|
||||
> Do not resume synchronizing this ledger. See [STATE.md](STATE.md) for the integrated checkpoint.
|
||||
|
||||
# Task Ledger — first release
|
||||
|
||||
## Current knowledge — 2026-09-10
|
||||
Updated 2026-09-11 for Codex-ytr83r. The full
|
||||
[user goal](../PROJECT_GOAL.md) and [current progress](STATE.md) remain authoritative.
|
||||
[Exact host ledger archives](history/2026-09-11-ytr83r-integration/README.md)
|
||||
preserve prior facts, dates and reasoning without transferring their acceptance.
|
||||
|
||||
Given: the renewed objective in `PROJECT_GOAL.md` requires a feature-complete,
|
||||
usable OA/SA first release and prioritizes direct behavior/scenario authoring.
|
||||
Graphical/source/REST parity, explicit reST semantics, traceability, offline
|
||||
guidance, Windows/Linux acceptance and independent review remain required.
|
||||
Older scope deferrals must be checked against this request.
|
||||
## Goal and given requirements
|
||||
|
||||
Verified: the checkout began clean at `1797516`; Gitea contains one active
|
||||
continuation issue and no open PR. Current code includes direct behavior editors,
|
||||
so old blanket descriptions of static diagrams need workflow-level reassessment.
|
||||
The machine began without the ignored historical toolchain/cache/distributions.
|
||||
Local Rust/Cargo 1.98.1 and verified LLVM-MinGW are now installed; dependency
|
||||
fetch is pending and no current binary has been built. Native Computer Use is
|
||||
available. Isolated Capella activity/lifeline/LOOP creation was saved and checked;
|
||||
no Arcadia window is running. Candidate B1/B2/B3 and F1 edits are source-complete,
|
||||
formatted and whitespace-clean, but have no fresh compile/test/runtime pass.
|
||||
Deliver a complete, usable OA/SA release on Windows and Linux, with one explicit
|
||||
reST semantic contract and equivalent graphical, textual and REST access. Include
|
||||
constraints/calculations, quantities/units, maintained libraries, behavior/state,
|
||||
traceability and navigable offline help. Reduce cognitive load and visual clutter;
|
||||
small and large models must be practical using text alone. Later Arcadia layers
|
||||
remain future scope. Compilation or a pleasant happy path is not completion.
|
||||
Issue2 further requires consistent direct process/scenario canvas interaction;
|
||||
the owner's clarification allows domain constraints rather than arbitrary movement.
|
||||
|
||||
Assumption: the selected compact architecture palette remains a promising design,
|
||||
pending real authoring evidence. Decision: repair and evaluate it before further
|
||||
redesign. Parallel independent audits identify current behavior and semantic gaps
|
||||
instead of reimplementing integrated work.
|
||||
|
||||
Open questions: which remaining menu-only behavior operations prevent a coherent
|
||||
direct workflow; which first-release semantic/parity gaps remain; and how to
|
||||
complete current Linux runtime acceptance. Root owns integration and native
|
||||
control; worker ownership is disjoint. See `STATE.md` and Gitea #1.
|
||||
|
||||
**Superseding user direction:** large horizontal creation buttons were rejected as intrusive and unprofessional. The user explicitly authorized consulting the installed Capella; that consultation has resumed and established actual compact palette, inline rename and explicit activity-allocation behavior. See [Capella 7.1 evidence and selected redesign](release-capella-basic-editing-20260907.md). The current candidate replaces creation/connection rows with one compact expandable palette and an in-place name editor. Native acceptance of the redesigned executable is pending; compactness is not assumed to prove usability. The earlier Escape hold and proposed toolbar-only correction below are superseded.
|
||||
|
||||
**Latest evidence:** basic editing full desktop584/584, bins, formatting and strictClippy passed. Native named Entity creation works at100%, but changing pending-toolbar height moves the screen placement22px. User Escape stopped the attempted Undo; native checks are paused again. Code-only toolbar correction proceeds. [Current findings](release-basic-editing-reset-20260907.md). The previous goal turn made concrete progress through implementation, integration gates and a native-discovered defect; this is not a no-progress blocker turn.
|
||||
|
||||
**Current steering:** basic diagram authoring and understandable OA/SA transitions come first. The user explicitly reauthorized desktop checks; root resumed the isolated test build and reproduced the reported usability problems. Creation, transition and independent fixture work is underway. No new basic-editing native pass is claimed. See [priority reset](release-basic-editing-reset-20260907.md); older execution priorities and Escape hold are superseded.
|
||||
|
||||
Updated 2026-09-07 after source7 Reset/ReadStatus and old6 recovery native passes, OA contextual persistence with rendering open, and reproducible Linux server test deadline evidence. Native Linux core now passed436/0failed/2ignored and its guest shut down cleanly. Old5 full recovery and source7 pending nested-link crash recovery/history/reopen also passed; routing3/3 and full desktop564/564 now pass. Root's private-server/full-server/format/strict-Clippy/bins sequence is running with results pending. [STATE.md](STATE.md) owns current execution/evidence and leases. [PLAN.md](PLAN.md) owns the release plan; [DECISIONS.md](DECISIONS.md) preserves architectural rationale. Full pre-consolidation contents, including superseded requirements interpretations and evidence, remain [archived byte-for-byte](history/2026-09-06-ledgers-before-consolidation/TASK_LEDGER.md).
|
||||
|
||||
## Goal and success criteria
|
||||
|
||||
The authoritative user objective is `C:\Users\User\.codex\attachments\10b0f701-1743-4de7-9ee1-225920e19649\goal-objective.md`. Deliver a usable, substantially complete OA/SA engineering tool in Rust/Iced on Windows and Linux; equivalent graphical, strict textual/reST and REST workflows; practical artifact traceability; navigable offline guidance; recoverable drafts; reproducible deployment. Completion requires native engineering task evidence and fresh independent end-state review, not implementation or passing tests alone. Later Arcadia layers remain future scope.
|
||||
|
||||
## Given facts and constraints
|
||||
|
||||
- User prioritizes Capella domain fidelity, useful containment/nesting and explicit typed ports, while avoiding clutter, persistent action overload and awkward navigation. Study real domain workflows rather than mechanically copying Capella classes.
|
||||
- Both-layer transitions and reviewed counterpart reconciliation must be intuitive and precise. Behavior/scenario diagrams must be directly editable and preserve selection/camera/task context.
|
||||
- Human/AI/Git-friendly modular source, comments/includes, complete graphical/source/API parity and clear rejection are required. One validated model/history boundary must govern every representation.
|
||||
- User permits autonomous reversible engineering, experiments, independent agents and authorized native work. Root is the sole native operator in this collaboration. Do not run concurrent shared Cargo writers. No remote push, usage reset, broad target deletion or host restart is implied by current task authorization.
|
||||
- User permits isolated Linux work. WSL remains unavailable pending restart; earlier task-local QEMU core and server gates completed and shut down cleanly; the separate source7 core gate also completed and shut down cleanly, as recorded below. The server built and passed real HTTP/restart checks; its full test command timed out during compilation before any test executed. Historical external worktrees/toolchains are not present on this machine.
|
||||
At15:28UTC on2026-09-11, the user explicitly deferred Linux builds and checking
|
||||
until they resolve WSL2 networking and instruct resumption. Linux setup/downloads
|
||||
are paused with inputs preserved. Continue independent Windows work; do not treat
|
||||
offline preparation as a networking fix or Linux acceptance.
|
||||
|
||||
## Verified facts
|
||||
|
||||
- Latest superseding checkpoint: Fit6 and both existing scroll/branch regressions, full desktop570/570, workspace bins, format and strict workspace/all-target Clippy pass. Windows preview565-file ZIP and complete examples are verified; current server56/56 also passed. Native BCC1 OA/SA nested routing and unchanged-source oracles passed, followed by bounded DC50 packaged Fit overview. Earlier pending-build/routing claims below retain their historical scope and are superseded by these exact current results in STATE.md.
|
||||
- Local start was cleanff615d0. Root decoded/hash-verified/imported published3978,
|
||||
c6 and4bb bundles; merged c6 as c81e285 and integrated creation4bb at e207b04. There were
|
||||
no production cherry-pick conflicts; docs/attributes require evidence union.
|
||||
The current goal is copied from the user's latest file.
|
||||
- Read-only integration comparison finds equivalent workspace/Sensor code in c6;
|
||||
replaying3978 or d114 is unnecessary. Core source is unchanged betweenff andc6.
|
||||
B4 now has a production Scene bridge and independently reviewed source, with
|
||||
focused lifecycle22/22 and full desktop675/675 passed on source2908405. The
|
||||
ordinary GUI build and real OA/SA lifecycle matrices now ran; OA full name-only
|
||||
Save/Undo/Redo/reopen and the corresponding SA workflow passed exact independent
|
||||
comparisons. Corrected workspace/all-targets strict Clippy also passed0;
|
||||
Unicode appearance remains open. See the
|
||||
[native record](release-lifecycle-native-ytr83r-20260911.md) for exact scope.
|
||||
- Imported source implements named behavior creation, exact prepared-batch
|
||||
correlation, success-only selection, raw-draft guards, recoverable original
|
||||
layer and modal-priority Escape. These are source facts; combined local runtime
|
||||
acceptance is pending.
|
||||
- Earlier-host desktop642 and653 suites, scoped native scenarios/creation,
|
||||
packaging25 notices and source experiments retain exact attribution in
|
||||
[STATE.md](STATE.md). They are not new local passes.
|
||||
- The original transferred creation oracle incorrectly equates physical disk files
|
||||
with reachable sources during Undo. Later external v2 corrects the evaluator
|
||||
with explicit origin/retained-byte rules; exact v2/evidence2ec was imported at
|
||||
b853a1e after complete path/hash preflight. New host execution is still pending.
|
||||
- Isolated Rust1.98.1/LLVM23.1.1 and locked dependencies are available. Packaging
|
||||
11/11 and synthetic Windows notice checks pass. Current core all-targets passed
|
||||
433 tests; its optional inheritance probe separately passed with slow debug
|
||||
timings that require controlled optimized interpretation. Focused lifecycle
|
||||
passed22/22 after two installed-Iced API corrections; full desktop passed675/675.
|
||||
Strict reST/HTML generation passes with Docutils0.22.2.
|
||||
- B4 independent criticism found and resolved self-caption overlap and two dense
|
||||
minimum-Fit hit-selection counterexamples. Shared nearest marker/message-stroke
|
||||
arbitration preserves exact marker/header/label selection. Ten real-Scene tests
|
||||
and two clip geometry tests passed in the22-test focused gate. The independent twelve-scenario native oracle passed compile,
|
||||
admission, self-check and exact baseline/name-only predictions before GUI output.
|
||||
- The separate baseline-context experiment passed218 assertions; explicit
|
||||
one-level overrides reduce repeated stable assumptions while complete frozen
|
||||
definition environments remain necessary. Proposed syntax is not source7.
|
||||
- After the user's virtualization/reboot change, supported Ubuntu26.04 install
|
||||
completed exit0 and a real x86_64 WSL2 kernel shell completed. Later VM startups
|
||||
timed out before apt. Owner328's exact VirtioProxy configuration now supports
|
||||
a persistent root shell, but requested IP/DNS/HTTPS checks failed. Signed offline
|
||||
dependency preparation advances without host VPN inspection or changes. This
|
||||
establishes guest execution, not Linux builds or GUI acceptance; exact outcomes are
|
||||
in the [WSL record](release-wsl-postboot-ytr83r-20260911.md).
|
||||
|
||||
- Branch/patch/tree audit established that direct state/scenario/process, data contracts, hosted editing and earlier semantic review are already integrated; no required production transplant was found. [Branch evidence](release-branch-integration-20260906.md).
|
||||
- Capella's function input/output exchange endpoints and component Standard/Flow ports are distinct; explicit allocation connects them. Preserve that domain distinction. [Native benchmark](release-capella-workflow-benchmark.md).
|
||||
- Source6 introduced participant CommunicationMean, typed explicit transition endpoint decisions, saved exchange categories/view selectors, completeness warnings and presentation duplication; current source7 retains them. Older audit absence claims are superseded; native acceptance remains scoped by reports.
|
||||
- Semantic review includes independent scalar/constructor/keyed/order/Child/Incident/artifact facets and multiple source rows. One frozen accepted base, explicit mappings, unique merged candidate slots and one atomic command preserve raw excluded/rejected drafts. Recovery replans; it never restores an unchecked serialized command. [Batch design](release-semantic-batch-foundation-design.md), [artifact contract](release-semantic-artifacts.md).
|
||||
- Native Windows artifact MOVE/CREATE/DELETE passed exact complete Save/Undo/Redo oracles, nine first-attempt saves. MOVE invalid raw optional state and CREATE exclusion/reserved identity survived controlled-stop recovery exactly. Destination-only retention is automated evidence, not separately native-applied in that run. [Native report](release-artifacts-native-20260906.md).
|
||||
- Native batch, Child/Incident and source-5 checkpoint workflows have scoped exact source/recovery/history evidence. Dense views have persistence/duplicate/hide and bounded readability evidence. Current counts, binaries and remaining cases are in STATE.md; these are not universal usability claims.
|
||||
- Native communication means drawing, explicit create/reuse ports and reverse Call/Reply pass independent complete model/source and exact Undo/Redo checks. The reverse navigation/overlapping-placement defect is fixed and reverified against a distinct oracle:10newUUIDs,zero implicit placements, correct Behavior owner and preserved Architecture. [Exact evidence](release-communication-native-20260906.md).
|
||||
- Windows semantic108desktop+2core/full527desktop passes remain scoped. Subsequent guide/home and behavior navigation slices passed focused tests/strict Clippy/build; semantic navigation is also natively reverified. Fresh source7 core all-targets433passed/1ignored across44 summaries and server55/55 across4 summaries have recorded exit0. Full desktop561/561 passed263.39s after a real scroll-state regression was fixed with a stable navigation Column; the unchanged test passed. Strict workspace/all-target Clippy and root-observed final format exit0 pass;3 affected semantic tests passed after equivalent lint edits. Subsequent routing3/3 and full desktop564/564 pass; new standalone build and strict/server results remain pending. Logs and exact scope are in STATE.md; do not add affected reruns to full-suite totals.
|
||||
- Native Scenario ordered review passed ambiguity rejection, explicit retained-local final order and complete Save/Undo/Redo source oracles at1100×700. Current guide binary passes related-topic heading reset, same-topic search scroll retention and Git auto-selected-topic reset without dirtying the model. Branch/fragment order variants and revised artifact diagnostic rendering remain separate gates. [Native ordered evidence](release-current-native-verification.md), [guide/diagnostic implementation](release-semantic-conflict-diagnostics-20260906.md).
|
||||
- Windows user-local GNU/LLVM builds and isolated clean-PATH native launch work. Actual Ubuntu24.04.4/Rust1.90 core/journal tests pass on hashed snapshots. Actual Linux server build and real HTTP modular import/invalidPUT/cleanrestart exact source/model/ETag pass on the older28-source lesson snapshot. Its full server test command timed out during compilation with zero tests executed, then that gate shut down cleanly. The current frozen source7 gate compiled the server in17m10s and passed unit2/2 plus HTTP50/51; the sole504-vs-412 timing assertion failed again alone. Its final persisted-state checks were not reached. Native core436passed/0failed/2ignored and the VM shut down cleanly08:13:56UTC; full desktop/Linux package remain unverified. [Linux evidence](release-linux-validation-options.md).
|
||||
- Corrected30-source/37-file teaching project passes minimum-size Home/guide/dirty-source guard, located initial warning, reviewed NameTake and complete exact source/Undo/Redo/reopen checks. The layer/canvas mismatch is corrected and natively reverified. Data/interface/type navigation, saved-view distinctions, invalid-view draft guard and typed state Entry fold/rename/history/reopen pass. The process workspace is cramped at minimum size and artifact navigation required excessive scrolling; targeted source7 UI improvements await native observation. [Native evidence](release-teaching-native-20260906.md).
|
||||
- Scope reconciliation established caller-context links into repeated/nested reusable chains (**S1**) and maintained interface-operation selection for parameterless messages (**S2**) as real missing semantics. Their ratified source7 implementation now has fresh full core/server/desktop gates, OpenAPI72 fixtures and strict reST evidence. Old5/6 compatibility and current7 raw recovery tests pass. Root's native Reset and ReadStatus cases passed complete Save/Undo/Redo oracles and reopened with explicit derived allocation30/31. OA First.B persistence/history/reopen passed but rendering is open; old6 recovery passed full30-source/model JSON and exact raw workspace/Ports DTO comparison. Old5 complete raw/accepted recovery, exact repaired30-source save/full-model export pass. Current7 invalid nested draft crash recovery and completed link Save/Undo/Redo/reopen also pass; corrected rendering and remaining native7/Linux workflows remain open. [Native report](release-source7-native-acceptance-20260907.md). [Scope reconciliation](release-scope-reconciliation-20260906.md), [integration decision](release-source7-integration-decision-20260906.md).
|
||||
## Assumptions and unresolved questions
|
||||
|
||||
## Decisions and candidate designs
|
||||
- Combining already coherent desktop slices should preserve behavior; fresh build,
|
||||
automated and native gates must test that assumption.
|
||||
- The baseline/library designs can fit existing atomic Project/Command/source
|
||||
boundaries; exact references, deletion, transition, imports, freezing and
|
||||
numeric rules still need ratified contracts and falsification.
|
||||
- Pinned package provenance must be distinguished from checked type compatibility;
|
||||
unchanged quantity kinds cannot become incompatible merely through cosmetic
|
||||
repackaging without a deliberate policy. Energy and Torque remain distinct.
|
||||
- Ubuntu26.04/WSL2 is the current supported Linux route; reliable repeat startup,
|
||||
guest toolchain/dependencies and GUI/service/package execution remain open.
|
||||
Initial shell success does not erase later startup timeouts. Old QEMU work is
|
||||
retained as history, not the next route to repeat.
|
||||
- Large-model human/AI task usability, cross-file refactoring, actual chooser
|
||||
latency/keyboard/minimum geometry and complete native lifecycle behavior remain
|
||||
to be measured. Parser admission or a widget experiment is insufficient.
|
||||
|
||||
Retain three Rust crates, one typed Project and validated atomic Command. Keep semantic ownership, reference/allocation/order relationships and diagram placement separate. Forms remain a closed typed adapter, not a second metamodel. Persistent additions require model validation, references/deletion, transition, parser/updater, API/schema and reachable graphical editing parity.
|
||||
## Candidate designs and selected directions
|
||||
|
||||
Semantic reconciliation uses finite typed facets and separate member/presence/order decisions. Compare actual destination slots before constructing the core's complete candidate; two rows never become sequential Apply. Missing identities are not inferred by names. Raw constructor/numeric/keyed/optional drafts survive rejection, exclusion and checkpoint recovery. Artifact Presence means attachment to the reviewed owner; an identified source moved away can delete the destination, whereas a source key of None preserves destination-only membership. No invented tombstone or silent seed acknowledgment.
|
||||
The imported [analysis proposal](release-analysis-semantics-design-20260910.md),
|
||||
[library proposal](release-libraries-semantics-design-20260910.md),
|
||||
[paired critique](release-semantic-proposals-review-20260910.md) and
|
||||
[textual authoring design](release-textual-authoring-design-20260911.md) retain
|
||||
alternatives and counterexamples. Metadata-only analysis and bare source includes
|
||||
do not meet evaluated quantities or maintained libraries.
|
||||
|
||||
Source updates preserve lexical ownership/comments/includes and exact history. Checkpoint source5/6→7 upgrade admits only the established token/typed migration contract, validates old base fingerprints and resets transient approvals; it does not silently rebase authored drafts. Hosted saves remain explicit and conditional on the resource/ETag; credentials stay in memory.
|
||||
Codex-956atf root selected property-owned baseline values/formulas with explicit
|
||||
ModelBaseline/Case evaluation and one-level case overrides for contract refinement.
|
||||
No hidden default case or UI-dependent evaluation; inherited formulas use the
|
||||
effective requested case, and complete freeze captures definitions/provenance.
|
||||
Exact numeric, library normalization/type compatibility and transition details
|
||||
remain open. This direction is recorded in [DECISIONS.md](DECISIONS.md), not silently
|
||||
treated as completed production semantics.
|
||||
|
||||
For Linux journal publication, ordinary rename lost the empty-target ownership invariant in a real ext4 probe. Selected `renameat2(RENAME_NOREPLACE)` through existing locked libc with no fallback; syscall errors fail before model writes. Alternative overwrite fallback was rejected by that falsifier. Windows native no-replace/retry behavior is retained; other OS behavior is explicit. [Contract, alternatives and measured limits](release-linux-journal-publication.md).
|
||||
The external staged prerequisite design reserves source/consumer identities,
|
||||
keeps the parent context/rawtext/slot, stages locally and creates source+consumer
|
||||
in one atomic final command. It must not implicitly allocate work, involve
|
||||
capabilities, retarget an Exchange-fixed endpoint or accept a different editor.
|
||||
Qualified Action allocation remains a distinct engineering choice.
|
||||
|
||||
Root selected **F1**, a complete source-6 teaching-project example. Corrected model/replay tests and bounded native onboarding/review/history pass; the full process/scenario/state/data/evidence lesson remains open. **F2**, faithful behavior-canvas SVG export, is deferred: current export scope is architecture-only, not a claim of faithful scenario/process/state export. Record this as an explicit scope decision rather than an accepted behavior-export result.
|
||||
## Retained implementation decisions
|
||||
|
||||
Behavior-root transition Apply should focus the explicitly requested counterpart and its nearest behavior owner. Without an explicit destination diagram it must preserve unrelated architecture presentation. Ordinary architecture transitions retain their existing path; explicit destination remains honored. This decision follows the independently reproduced reverse scenario placement/navigation failure, not a semantic change to the core transition.
|
||||
- Keep one authoritative typed Project, validated atomic Commands, stable
|
||||
identities and explicit references. Semantic ownership/allocation/order and
|
||||
diagram geometry remain distinct; forms are adapters, not a second metamodel.
|
||||
- Preserve source ownership/comments/includes and exact model/source history.
|
||||
Writeback succeeds before adoption. Invalid/rejected raw drafts survive and
|
||||
browsing/tool selection does not silently accept another editor. Recovery
|
||||
validates/replans rather than trusting serialized commands.
|
||||
- Semantic reconciliation uses finite typed facets, frozen baselines, explicit
|
||||
mappings and one atomic candidate. Names alone never establish identity.
|
||||
- OA↔SA transitions are explicit engineering decisions. New persistent semantics
|
||||
need validation/reference/deletion/transition/source/API/UI/history parity.
|
||||
- Exact evidence bytes and host attribution survive integration. No focused
|
||||
test total is added to a full-suite count; old binary acceptance does not
|
||||
transfer to newly merged code.
|
||||
|
||||
## Assumptions and open questions
|
||||
## Current strategy and coordination
|
||||
|
||||
- Nested source and contextual editors should reduce authoring effort; additional shorthand/grouping must justify grammar or interaction complexity through real tasks. Dense-view path overlap is not itself a failure when every actual identity remains accessible; arbitrary crossing-free layout is not promised.
|
||||
- Remaining native variants must establish useful engineering workflows: CM transitions, advanced OA process/SA chain/scenario reuse/fragment/order, typed state actions, compact/Linux/hosted cases, and uncovered saved-view lifecycle combinations. Do not expand or shrink required OA/SA scope merely to fit implementation convenience.
|
||||
- Latest diagnostic/reserved-label corrections have independent review and completed semantic/full-desktop/Clippy/build checks; their revised artifact rendering still needs native verification. The historical source6 process3012 closed cleanly. The6BAB source7 native build passed the scoped cases above;11060 and old6 recovery11240 closed cleanly. Old5 recovery/repair/save/model and current7 nested draft recovery/history/reopen pass on that binary. Later routing/hit-priority changes passed3focused/full564 desktop tests; root is running the server/format/strict/build sequence with results pending, and no corrected-render native acceptance is transferred. Exact binary scope is in STATE.md.
|
||||
- Historical Linux evidence includes x86_64/glibc2.39/ext4 core/MSRV and server/HTTP runtime. Current455-file snapshot SHA256 `7104946c89b2be7a1a030fdff47cb4405bacf05357d68cf35d181e860f55c279` is frozen; core completed436passes/2ignored before clean VM shutdown08:13:56UTC; its original08:32:43UTC deadline was not reached. The hash-verified cross-built probe executed successfully on Linux, including its Windows macro/build-script build path, but no complete Linux GUI has been built. Syscall failure tests do not establish power-loss durability, network-filesystem behavior or cross-process model CAS. [Current Linux gate](release-source7-linux-gate-20260907.md).
|
||||
- Earlier Windows QA packages include runtime/notices and verified archive contents; final accepted-tree optimized distributions and exact provenance/checksums remain a release gate. [Packaging evidence](release-windows-package-review.md).
|
||||
Root owns local integration, serialized builds, native inputs and final synthesis.
|
||||
The local audit resolves evidence and reviews source; independent server review
|
||||
has its own report. Codex-9e8r6t owns staged sources/first actions and source CLI;
|
||||
Codex-956atf owns chooser, semantic contracts and the shared canvas design. Read Gitea issues #1/#2 and Wiki before
|
||||
resuming, obtain exact coherent transfer heads, and respect their source ownership.
|
||||
|
||||
## Current strategy
|
||||
|
||||
The requested Windows preview and whole-run development summary are delivered; the summary is being refreshed with the confirmed package, full570/570 desktop result and final Fit/strict/build gates. Preview DC50 has bounded native launch/overview evidence, not complete minimum-height/pending-form acceptance. Computer Use stopped on user Escape, so leave live GUI processes untouched and advance independent verification.
|
||||
|
||||
Preserve the successful immutable458-file Linux GUI cross-build with the Fit fix, exact ELF/package evidence and released compiler lease. Three subsequent guest boots reached terminal IO-APIC timer panic before any tests; exact-kernel extraction succeeded but automatic approval review denied the proposed transient workaround. All task VMs are stopped. Preserve the denied action and evidence without bypassing review. Its snapshot/run/hashes and actual process status belong in STATE.md and the cross-build report. Preserve the original interrupted runs, cache, native core436/2ignored and failed Linux timed-server fixture. The replacement private HTTP preparation test and full server56 pass on Windows; Linux execution remains required.
|
||||
|
||||
Guide steps now name Tools/Selection details/Restore panels and the exact Reply label; formatting, independent label review and a fresh Windows workspace-binary build pass. The user-facing preview retains its original tested bytes. Independent full-model/source purpose oracles compiled and passed both self-checks and preserved baseline cases; complete their native authoring/history steps when control resumes. Complete current native OA/SA teaching, artifact, saved-view, process/scenario and hosted cases once control is available. Retain caller-specific budgets, actual port proof, independent channel validation and exact old5/6 recovery bytes. Final optimized packages, current platform gates and fresh independent end-state review remain open; architecture-only SVG and passing slices do not redefine completion.
|
||||
First finish and verify the integrated current source using the imported corrected
|
||||
evaluator and newly prepared independent lifetime oracle. Then close remaining modeling/textual,
|
||||
analysis/library and platform gates with independent criticism and whole-task
|
||||
acceptance. Update facts, assumptions and decisions as evidence changes them.
|
||||
|
||||
@@ -0,0 +1,676 @@
|
||||
//! Independent native creation oracle; no desktop producer is imported.
|
||||
//! self-check BASE | check STAGE BASE ACTUAL ROLES | same EXPECTED ACTUAL [UNDO_FROM]
|
||||
use oa_core::{source_project::*, *};
|
||||
use std::{
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
fs,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
const PROCESS: &str = "Coordinate rescue";
|
||||
const SCENARIO: &str = "Rescue coordination conversation";
|
||||
const MACHINE: &str = "Rescue team operating modes";
|
||||
const VIEW: &str = "Rescue team operating modes · states";
|
||||
const CAPABILITY: u128 = 0xb451c00000004000800000000000001f;
|
||||
const OWNER: u128 = 0xc062944912094b50afacf558a9a0fc7f;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum Stage {
|
||||
Baseline,
|
||||
Process,
|
||||
Scenario,
|
||||
Machine,
|
||||
}
|
||||
impl Stage {
|
||||
fn parse(value: &str) -> Self {
|
||||
match value {
|
||||
"baseline" => Self::Baseline,
|
||||
"process" => Self::Process,
|
||||
"scenario" => Self::Scenario,
|
||||
"machine" => Self::Machine,
|
||||
_ => panic!("Stage must be baseline, process, scenario or machine"),
|
||||
}
|
||||
}
|
||||
fn additions(self) -> usize {
|
||||
match self {
|
||||
Self::Baseline => 0,
|
||||
Self::Process => 1,
|
||||
Self::Scenario => 2,
|
||||
Self::Machine => 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type DiskSources = BTreeMap<String, Vec<u8>>;
|
||||
|
||||
fn source_paths(root: &Path, here: &Path, paths: &mut DiskSources) {
|
||||
for entry in fs::read_dir(here).unwrap() {
|
||||
let entry = entry.unwrap();
|
||||
let kind = entry.file_type().unwrap();
|
||||
assert!(
|
||||
!kind.is_symlink(),
|
||||
"Native checkpoint must contain ordinary files"
|
||||
);
|
||||
if kind.is_dir() {
|
||||
source_paths(root, &entry.path(), paths);
|
||||
} else if entry.path().extension().is_some_and(|ext| ext == "arcadia") {
|
||||
assert!(kind.is_file());
|
||||
paths.insert(
|
||||
entry
|
||||
.path()
|
||||
.strip_prefix(root)
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.replace('\\', "/"),
|
||||
fs::read(entry.path()).unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn load_checkpoint(entry: &Path) -> (CompiledSourceProject, DiskSources) {
|
||||
let files = source_files::load_project(entry, ProjectLimits::default()).unwrap();
|
||||
let raw = files.reachable_bundle();
|
||||
let compiled = compile_bundle(&raw, ProjectLimits::default()).unwrap();
|
||||
assert_eq!(
|
||||
compiled.bundle, raw,
|
||||
"No migration, normalization or implicit identity insertion"
|
||||
);
|
||||
execute_batch(&compiled.decoded.project, &[]).unwrap();
|
||||
let mut disk = BTreeMap::new();
|
||||
let root = entry.parent().unwrap();
|
||||
source_paths(root, root, &mut disk);
|
||||
(compiled, disk)
|
||||
}
|
||||
fn predicted_disk(reachable: &SourceBundle, undo_from: Option<&DiskSources>) -> DiskSources {
|
||||
// Saving Undo writes the restored reachable files and deliberately keeps
|
||||
// detached files from the previously saved checkpoint. Never infer their
|
||||
// bytes from the actual Undo directory: its explicit origin is the oracle.
|
||||
let mut expected = undo_from.cloned().unwrap_or_default();
|
||||
expected.extend(
|
||||
reachable
|
||||
.files
|
||||
.iter()
|
||||
.map(|(path, source)| (path.clone(), source.as_bytes().to_vec())),
|
||||
);
|
||||
expected
|
||||
}
|
||||
fn load(entry: &Path) -> CompiledSourceProject {
|
||||
let (compiled, disk) = load_checkpoint(entry);
|
||||
assert_eq!(
|
||||
disk,
|
||||
predicted_disk(&compiled.bundle, None),
|
||||
"Ordinary checkpoints must contain exactly their reachable source files; retained Undo files require an explicit origin"
|
||||
);
|
||||
compiled
|
||||
}
|
||||
fn fnv1a(bytes: &[u8]) -> u64 {
|
||||
bytes.iter().fold(0xcbf29ce484222325, |hash, byte| {
|
||||
(hash ^ u64::from(*byte)).wrapping_mul(0x100000001b3)
|
||||
})
|
||||
}
|
||||
fn pinned_baseline(base: &CompiledSourceProject) -> bool {
|
||||
// Fixed byte fingerprints of the root-preserved transition/reopen input,
|
||||
// recorded before any creation. SHA-256 provenance is in the fixture README.
|
||||
let pins = [
|
||||
("project.arcadia", 4844, 0x450c6ffd8225af32),
|
||||
("rationale.arcadia", 57, 0xf87e3c2e303f9b62),
|
||||
("traceability.arcadia", 395, 0xb47992dbdacab344),
|
||||
("oa/participants.arcadia", 403, 0x1e109eef81968b5f),
|
||||
(
|
||||
"oa/activities/activity_assess_incident.arcadia",
|
||||
376,
|
||||
0x0e67e533db3cff78,
|
||||
),
|
||||
("sa/participants.arcadia", 124, 0xcadb35025aea18cc),
|
||||
];
|
||||
base.bundle.entry == "project.arcadia"
|
||||
&& base.bundle.files.len() == pins.len()
|
||||
&& pins.iter().all(|(path, length, hash)| {
|
||||
base.bundle
|
||||
.files
|
||||
.get(*path)
|
||||
.is_some_and(|text| text.len() == *length && fnv1a(text.as_bytes()) == *hash)
|
||||
})
|
||||
}
|
||||
fn baseline(entry: &Path) -> CompiledSourceProject {
|
||||
let base = load(entry);
|
||||
assert!(
|
||||
pinned_baseline(&base),
|
||||
"The six-file baseline differs from the frozen pre-creation input"
|
||||
);
|
||||
let p = &base.decoded.project;
|
||||
assert_eq!(
|
||||
(
|
||||
p.elements.len(),
|
||||
p.relations.len(),
|
||||
p.diagrams.len(),
|
||||
p.traces.len()
|
||||
),
|
||||
(10, 2, 4, 1)
|
||||
);
|
||||
let context = &p.elements[&Id::from_u128(CAPABILITY)];
|
||||
assert_eq!(
|
||||
(
|
||||
context.name.as_str(),
|
||||
context.kind(),
|
||||
context.layer(),
|
||||
context.parent
|
||||
),
|
||||
("Rescue capability", Kind::Capability, Layer::Oa, None)
|
||||
);
|
||||
let owner = &p.elements[&Id::from_u128(OWNER)];
|
||||
assert_eq!(
|
||||
(
|
||||
owner.name.as_str(),
|
||||
owner.kind(),
|
||||
owner.layer(),
|
||||
owner.parent
|
||||
),
|
||||
("Rescue team", Kind::SystemActor, Layer::Sa, None)
|
||||
);
|
||||
for name in [PROCESS, SCENARIO, MACHINE] {
|
||||
assert!(
|
||||
!p.elements.values().any(|e| e.name == name),
|
||||
"Baseline already contains requested creation"
|
||||
);
|
||||
}
|
||||
base
|
||||
}
|
||||
|
||||
struct Roles<'a> {
|
||||
actual: &'a Project,
|
||||
baseline: &'a Project,
|
||||
used: BTreeSet<Id>,
|
||||
}
|
||||
impl Roles<'_> {
|
||||
fn fresh(&mut self, choices: Vec<Id>, role: &str) -> Id {
|
||||
assert_eq!(
|
||||
choices.len(),
|
||||
1,
|
||||
"Exactly one literal identity role required: {role}"
|
||||
);
|
||||
let id = choices[0];
|
||||
assert_eq!(id.get_version_num(), 4, "{role} must be a fresh UUIDv4");
|
||||
assert!(
|
||||
!self.baseline.contains(id),
|
||||
"{role} reuses a baseline identity"
|
||||
);
|
||||
assert!(self.used.insert(id), "{role} reuses another fresh role");
|
||||
id
|
||||
}
|
||||
fn element(&mut self, kind: Kind, layer: Layer, name: &str) -> Id {
|
||||
self.fresh(
|
||||
self.actual
|
||||
.elements
|
||||
.values()
|
||||
.filter(|e| e.kind() == kind && e.layer() == layer && e.name == name)
|
||||
.map(|e| e.id)
|
||||
.collect(),
|
||||
name,
|
||||
)
|
||||
}
|
||||
fn view(&mut self, machine: Id) -> Id {
|
||||
self.fresh(
|
||||
self.actual
|
||||
.diagrams
|
||||
.values()
|
||||
.filter(|d| {
|
||||
d.layer == Layer::Sa
|
||||
&& d.name == VIEW
|
||||
&& d.projection == ProjectionKind::StateMachine { machine }
|
||||
})
|
||||
.map(|d| d.id)
|
||||
.collect(),
|
||||
VIEW,
|
||||
)
|
||||
}
|
||||
fn node(&mut self, diagram: Id, machine: Id) -> Id {
|
||||
self.fresh(
|
||||
self.actual.diagrams[&diagram]
|
||||
.nodes
|
||||
.values()
|
||||
.filter(|node| node.element == machine)
|
||||
.map(|node| node.id)
|
||||
.collect(),
|
||||
"new machine's exact view placement",
|
||||
)
|
||||
}
|
||||
}
|
||||
fn element(p: &Project, id: Id, name: &str, parent: Option<Id>, body: ElementBody) -> Element {
|
||||
Element {
|
||||
id,
|
||||
alias: p.fresh_alias(name, "behavior", id),
|
||||
name: name.into(),
|
||||
description: String::new(),
|
||||
parent,
|
||||
body,
|
||||
}
|
||||
}
|
||||
fn advance(base: &mut CompiledSourceProject, commands: Vec<Command>) {
|
||||
let project = execute_batch(&base.decoded.project, &[Command::Batch(commands)]).unwrap();
|
||||
*base = update_bundle(base, &project, ProjectLimits::default()).unwrap();
|
||||
}
|
||||
fn predict(base: &CompiledSourceProject, stage: Stage, carrier: &Project) -> CompiledSourceProject {
|
||||
let mut result = base.clone();
|
||||
let mut roles = Roles {
|
||||
actual: carrier,
|
||||
baseline: &base.decoded.project,
|
||||
used: BTreeSet::new(),
|
||||
};
|
||||
if stage >= Stage::Process {
|
||||
let id = roles.element(Kind::Process, Layer::Oa, PROCESS);
|
||||
let record = element(
|
||||
&result.decoded.project,
|
||||
id,
|
||||
PROCESS,
|
||||
None,
|
||||
ElementBody::Oa(OaBody::Process { steps: vec![] }),
|
||||
);
|
||||
advance(&mut result, vec![Command::CreateElement(record)]);
|
||||
}
|
||||
if stage >= Stage::Scenario {
|
||||
let id = roles.element(Kind::Scenario, Layer::Oa, SCENARIO);
|
||||
let record = element(
|
||||
&result.decoded.project,
|
||||
id,
|
||||
SCENARIO,
|
||||
None,
|
||||
ElementBody::shared_in(
|
||||
Layer::Oa,
|
||||
SharedBody::Scenario {
|
||||
viewpoint: ScenarioViewpoint::ParticipantInteraction,
|
||||
context: Some(Id::from_u128(CAPABILITY)),
|
||||
precondition: String::new(),
|
||||
postcondition: String::new(),
|
||||
steps: vec![],
|
||||
},
|
||||
),
|
||||
);
|
||||
advance(&mut result, vec![Command::CreateElement(record)]);
|
||||
}
|
||||
if stage == Stage::Machine {
|
||||
let machine = roles.element(Kind::StateMachine, Layer::Sa, MACHINE);
|
||||
let diagram = roles.view(machine);
|
||||
let placement = roles.node(diagram, machine);
|
||||
let record = element(
|
||||
&result.decoded.project,
|
||||
machine,
|
||||
MACHINE,
|
||||
Some(Id::from_u128(OWNER)),
|
||||
ElementBody::shared_in(Layer::Sa, SharedBody::StateMachine),
|
||||
);
|
||||
let p = execute_batch(
|
||||
&result.decoded.project,
|
||||
&[Command::CreateElement(record.clone())],
|
||||
)
|
||||
.unwrap();
|
||||
// Published first-view contract, independently constructed: one root,
|
||||
// default options, no routes/regions/states. Shared core Layout owns the
|
||||
// final geometry; no observed geometry or desktop planner is consulted.
|
||||
let view = Diagram {
|
||||
id: diagram,
|
||||
alias: p.fresh_alias(VIEW, "state_view", diagram),
|
||||
name: VIEW.into(),
|
||||
description: String::new(),
|
||||
layer: Layer::Sa,
|
||||
projection: ProjectionKind::StateMachine { machine },
|
||||
routes: vec![],
|
||||
view_options: Default::default(),
|
||||
nodes: BTreeMap::from([(
|
||||
placement,
|
||||
NodePlacement {
|
||||
id: placement,
|
||||
alias: p.fresh_alias(&format!("{MACHINE} placement"), "placement", placement),
|
||||
element: machine,
|
||||
position: Position { x: 60., y: 60. },
|
||||
size: Size {
|
||||
width: 336.,
|
||||
height: 104.,
|
||||
},
|
||||
collapsed: false,
|
||||
port_anchors: vec![],
|
||||
},
|
||||
)]),
|
||||
};
|
||||
advance(
|
||||
&mut result,
|
||||
vec![
|
||||
Command::CreateElement(record),
|
||||
Command::CreateDiagram(view),
|
||||
Command::Layout(diagram),
|
||||
],
|
||||
);
|
||||
}
|
||||
let p = &result.decoded.project;
|
||||
assert_eq!(
|
||||
p.elements.len(),
|
||||
base.decoded.project.elements.len() + stage.additions()
|
||||
);
|
||||
assert_eq!(
|
||||
p.diagrams.len(),
|
||||
base.decoded.project.diagrams.len() + usize::from(stage == Stage::Machine)
|
||||
);
|
||||
assert_eq!(p.relations, base.decoded.project.relations);
|
||||
assert_eq!(p.traces, base.decoded.project.traces);
|
||||
result
|
||||
}
|
||||
fn equal(left: &CompiledSourceProject, right: &CompiledSourceProject) -> bool {
|
||||
left.decoded.project == right.decoded.project && left.bundle == right.bundle
|
||||
}
|
||||
fn assert_equal(expected: &CompiledSourceProject, actual: &CompiledSourceProject) {
|
||||
assert_eq!(
|
||||
actual.decoded.project, expected.decoded.project,
|
||||
"Complete Project differs from independent prediction"
|
||||
);
|
||||
assert_eq!(
|
||||
actual.bundle, expected.bundle,
|
||||
"Complete SourceBundle differs, including comments and all included bytes"
|
||||
);
|
||||
}
|
||||
fn test_id(n: u128) -> Id {
|
||||
Id::from_u128(0xc4ea7100000040008000000000000000 + n)
|
||||
}
|
||||
fn false_carrier(base: &Project) -> Project {
|
||||
let mut p = base.clone();
|
||||
for (n, kind, layer, name) in [
|
||||
(1, Kind::Process, Layer::Oa, PROCESS),
|
||||
(2, Kind::Scenario, Layer::Oa, SCENARIO),
|
||||
(3, Kind::StateMachine, Layer::Sa, MACHINE),
|
||||
] {
|
||||
p.elements.insert(
|
||||
test_id(n),
|
||||
Element {
|
||||
id: test_id(n),
|
||||
alias: format!("false_alias_{n}"),
|
||||
name: name.into(),
|
||||
description: "Must never be copied".into(),
|
||||
parent: None,
|
||||
body: ElementBody::new(kind, layer).unwrap(),
|
||||
},
|
||||
);
|
||||
}
|
||||
p.diagrams.insert(
|
||||
test_id(4),
|
||||
Diagram {
|
||||
id: test_id(4),
|
||||
alias: "false_view_alias".into(),
|
||||
name: VIEW.into(),
|
||||
description: "False view description".into(),
|
||||
layer: Layer::Sa,
|
||||
projection: ProjectionKind::StateMachine {
|
||||
machine: test_id(3),
|
||||
},
|
||||
routes: vec![],
|
||||
view_options: Default::default(),
|
||||
nodes: BTreeMap::from([(
|
||||
test_id(5),
|
||||
NodePlacement {
|
||||
id: test_id(5),
|
||||
alias: "false_node_alias".into(),
|
||||
element: test_id(3),
|
||||
position: Position {
|
||||
x: 9999.,
|
||||
y: -9999.,
|
||||
},
|
||||
size: Size {
|
||||
width: 1.,
|
||||
height: 1.,
|
||||
},
|
||||
collapsed: true,
|
||||
port_anchors: vec![],
|
||||
},
|
||||
)]),
|
||||
},
|
||||
);
|
||||
p
|
||||
}
|
||||
fn self_check(base: &CompiledSourceProject) {
|
||||
let original = base.clone();
|
||||
let carrier = false_carrier(&base.decoded.project);
|
||||
assert_equal(base, &predict(base, Stage::Baseline, &carrier));
|
||||
for stage in [Stage::Process, Stage::Scenario, Stage::Machine] {
|
||||
let expected = predict(base, stage, &carrier);
|
||||
assert_equal(&expected, &predict(base, stage, &expected.decoded.project));
|
||||
let parsed = compile_bundle(&expected.bundle, ProjectLimits::default()).unwrap();
|
||||
assert_equal(&expected, &parsed);
|
||||
let mut altered = expected.clone();
|
||||
altered
|
||||
.decoded
|
||||
.project
|
||||
.elements
|
||||
.get_mut(&test_id(1))
|
||||
.unwrap()
|
||||
.description = "Unrequested metadata".into();
|
||||
assert!(!equal(&expected, &altered));
|
||||
let mut altered = expected.clone();
|
||||
altered
|
||||
.decoded
|
||||
.project
|
||||
.elements
|
||||
.get_mut(&Id::from_u128(CAPABILITY))
|
||||
.unwrap()
|
||||
.name = "Changed unrelated baseline".into();
|
||||
assert!(!equal(&expected, &altered));
|
||||
let mut changed_source = expected.bundle.clone();
|
||||
changed_source
|
||||
.files
|
||||
.get_mut("rationale.arcadia")
|
||||
.unwrap()
|
||||
.push_str("// Extra comment\n");
|
||||
let altered = compile_bundle(&changed_source, ProjectLimits::default()).unwrap();
|
||||
assert_eq!(altered.decoded.project, expected.decoded.project);
|
||||
assert!(
|
||||
!equal(&expected, &altered),
|
||||
"Identical semantics do not excuse changed source bytes"
|
||||
);
|
||||
if stage >= Stage::Scenario {
|
||||
let mut altered = expected.clone();
|
||||
*altered
|
||||
.decoded
|
||||
.project
|
||||
.elements
|
||||
.get_mut(&test_id(2))
|
||||
.unwrap()
|
||||
.shared_mut()
|
||||
.unwrap() = SharedBody::Scenario {
|
||||
viewpoint: ScenarioViewpoint::OperationalActivity,
|
||||
context: None,
|
||||
precondition: String::new(),
|
||||
postcondition: String::new(),
|
||||
steps: vec![],
|
||||
};
|
||||
assert!(!equal(&expected, &altered));
|
||||
}
|
||||
if stage == Stage::Machine {
|
||||
assert_ne!(
|
||||
expected.decoded.project, carrier,
|
||||
"Never adopt role-carrier metadata/geometry"
|
||||
);
|
||||
assert_eq!(
|
||||
expected.decoded.project.elements[&test_id(3)].parent,
|
||||
Some(Id::from_u128(OWNER))
|
||||
);
|
||||
let view = &expected.decoded.project.diagrams[&test_id(4)];
|
||||
assert_eq!(view.nodes.len(), 1);
|
||||
let node = &view.nodes[&test_id(5)];
|
||||
println!(
|
||||
"PREDICT first view: {VIEW}; machine-root position {:?}, size {:?}; {} exact source files",
|
||||
node.position,
|
||||
node.size,
|
||||
expected.bundle.files.len()
|
||||
);
|
||||
let mut altered = expected.clone();
|
||||
altered
|
||||
.decoded
|
||||
.project
|
||||
.diagrams
|
||||
.get_mut(&test_id(4))
|
||||
.unwrap()
|
||||
.nodes
|
||||
.get_mut(&test_id(5))
|
||||
.unwrap()
|
||||
.position
|
||||
.x += 1.;
|
||||
assert!(!equal(&expected, &altered));
|
||||
let mut altered = expected.clone();
|
||||
let extra = element(
|
||||
&altered.decoded.project,
|
||||
test_id(6),
|
||||
"Unrequested region",
|
||||
Some(test_id(3)),
|
||||
ElementBody::shared_in(Layer::Sa, SharedBody::Region),
|
||||
);
|
||||
altered.decoded.project.elements.insert(extra.id, extra);
|
||||
assert!(!equal(&expected, &altered), "Reject extra semantic objects");
|
||||
let mut altered = expected.clone();
|
||||
altered
|
||||
.bundle
|
||||
.files
|
||||
.insert("unrequested.arcadia".into(), "// Extra file\n".into());
|
||||
assert!(!equal(&expected, &altered));
|
||||
}
|
||||
println!(
|
||||
"PASS self-check {stage:?}: full source fixed point; false metadata, unrelated edits and source-only drift rejected"
|
||||
);
|
||||
}
|
||||
let mut altered_base = base.clone();
|
||||
altered_base
|
||||
.bundle
|
||||
.files
|
||||
.get_mut("rationale.arcadia")
|
||||
.unwrap()
|
||||
.push(' ');
|
||||
assert!(
|
||||
!pinned_baseline(&altered_base),
|
||||
"Reject an altered baseline before use"
|
||||
);
|
||||
assert_equal(&original, base);
|
||||
let before = predict(base, Stage::Scenario, &carrier);
|
||||
let created = predict(base, Stage::Machine, &carrier);
|
||||
let origin = predicted_disk(&created.bundle, None);
|
||||
let detached: Vec<_> = created
|
||||
.bundle
|
||||
.files
|
||||
.keys()
|
||||
.filter(|path| !before.bundle.files.contains_key(*path))
|
||||
.cloned()
|
||||
.collect();
|
||||
assert_eq!(
|
||||
detached.len(),
|
||||
1,
|
||||
"A machine creation adds one source module"
|
||||
);
|
||||
// Independently simulate the saver's observable result: restored reachable
|
||||
// bytes plus the one new module left on disk by the prior creation.
|
||||
let mut retained = predicted_disk(&before.bundle, None);
|
||||
retained.insert(
|
||||
detached[0].clone(),
|
||||
created.bundle.files[&detached[0]].as_bytes().to_vec(),
|
||||
);
|
||||
assert_eq!(
|
||||
retained[&detached[0]], origin[&detached[0]],
|
||||
"Retained bytes must come from the exact created checkpoint"
|
||||
);
|
||||
assert_ne!(
|
||||
retained,
|
||||
predicted_disk(&before.bundle, None),
|
||||
"Ordinary comparison must not accept detached history files"
|
||||
);
|
||||
assert_eq!(
|
||||
retained,
|
||||
predicted_disk(&before.bundle, Some(&origin)),
|
||||
"Explicit Undo origin permits only the expected retained bytes"
|
||||
);
|
||||
let mut unknown = retained.clone();
|
||||
unknown.insert("unknown.arcadia".into(), b"// Unclaimed file\n".to_vec());
|
||||
assert_ne!(
|
||||
unknown,
|
||||
predicted_disk(&before.bundle, Some(&origin)),
|
||||
"Reject unknown detached files"
|
||||
);
|
||||
let mut changed = retained.clone();
|
||||
changed.get_mut(&detached[0]).unwrap().push(b' ');
|
||||
assert_ne!(
|
||||
changed,
|
||||
predicted_disk(&before.bundle, Some(&origin)),
|
||||
"Reject changed detached bytes"
|
||||
);
|
||||
let mut missing = retained.clone();
|
||||
missing.remove(&detached[0]);
|
||||
assert_ne!(
|
||||
missing,
|
||||
predicted_disk(&before.bundle, Some(&origin)),
|
||||
"The retained file set is exact, not optional"
|
||||
);
|
||||
let mut reachable_drift = retained.clone();
|
||||
reachable_drift
|
||||
.get_mut("rationale.arcadia")
|
||||
.unwrap()
|
||||
.push(b' ');
|
||||
assert_ne!(
|
||||
reachable_drift,
|
||||
predicted_disk(&before.bundle, Some(&origin)),
|
||||
"An Undo exemption cannot hide reachable-source changes"
|
||||
);
|
||||
println!(
|
||||
"PASS explicit Undo disk contract: known detached retention accepted; unknown, changed, missing and reachable drift rejected"
|
||||
);
|
||||
println!("PASS baseline retained unchanged; no native acceptance claimed");
|
||||
}
|
||||
fn main() {
|
||||
assert_eq!(SCHEMA_VERSION, 7);
|
||||
let args: Vec<_> = std::env::args().collect();
|
||||
match args.get(1).map(String::as_str) {
|
||||
Some("self-check") => {
|
||||
assert_eq!(args.len(), 3, "self-check BASELINE_ENTRY");
|
||||
self_check(&baseline(Path::new(&args[2])));
|
||||
}
|
||||
Some("check") => {
|
||||
assert_eq!(
|
||||
args.len(),
|
||||
6,
|
||||
"check baseline|process|scenario|machine BASELINE_ENTRY ACTUAL_ENTRY ROLE_ENTRY"
|
||||
);
|
||||
let stage = Stage::parse(&args[2]);
|
||||
let base = baseline(Path::new(&args[3]));
|
||||
let actual = load(Path::new(&args[4]));
|
||||
let carrier = load(Path::new(&args[5]));
|
||||
let expected = predict(&base, stage, &carrier.decoded.project);
|
||||
assert_equal(&expected, &actual);
|
||||
println!(
|
||||
"PASS {stage:?}: complete Project + {} exact source files; only fresh literal-role UUIDs borrowed",
|
||||
actual.bundle.files.len()
|
||||
);
|
||||
}
|
||||
Some("same") => {
|
||||
assert!(
|
||||
matches!(args.len(), 4 | 5),
|
||||
"same EXPECTED_CHECKPOINT ACTUAL_CHECKPOINT [UNDO_FROM_CHECKPOINT]"
|
||||
);
|
||||
let expected = load(Path::new(&args[2]));
|
||||
let (actual, disk) = load_checkpoint(Path::new(&args[3]));
|
||||
assert_equal(&expected, &actual);
|
||||
let origin = args.get(4).map(|entry| {
|
||||
// The first committed checkpoint is itself exact. This keeps
|
||||
// arbitrary pre-existing detached files out of the exception.
|
||||
let committed = load(Path::new(entry));
|
||||
predicted_disk(&committed.bundle, None)
|
||||
});
|
||||
assert_eq!(
|
||||
disk,
|
||||
predicted_disk(&expected.bundle, origin.as_ref()),
|
||||
"Disk source files differ from the exact checkpoint plus explicitly proven retained Undo files"
|
||||
);
|
||||
println!(
|
||||
"PASS exact checkpoint identity/model/{}-file SourceBundle equality; {} exact disk files ({} known retained)",
|
||||
actual.bundle.files.len(),
|
||||
disk.len(),
|
||||
disk.len() - actual.bundle.files.len()
|
||||
);
|
||||
}
|
||||
_ => panic!(
|
||||
"self-check BASE | check STAGE BASE ACTUAL ROLES | same EXPECTED ACTUAL [UNDO_FROM]"
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
//! Native issue4 fixture/oracle. generate DIR | check oa|sa STAGE ENTRY
|
||||
//! snapshot ENTRY DIR | compare ENTRY DIR | self-check
|
||||
use oa_core::{source_project::*, *};
|
||||
use std::{collections::{BTreeMap, BTreeSet}, fs, path::Path};
|
||||
fn id(n: u128) -> Id { Id::from_u128(0xc1140000000040008000000000000000 + n) }
|
||||
fn work(layer: Layer) -> Kind { if layer == Layer::Oa { Kind::Activity } else { Kind::Function } }
|
||||
fn fixture(layer: Layer) -> Project {
|
||||
let mut p = Project::empty();
|
||||
p.id = id(1); p.alias = "child_work_native".into(); p.name = format!("Issue4 {layer:?} child work");
|
||||
for (n, kind, name) in [(10, if layer == Layer::Oa { Kind::Entity } else { Kind::System }, "Performer"), (11, work(layer), "Parent work")] {
|
||||
p.elements.insert(id(n), Element { id: id(n), alias: format!("seed_{n}"), name: name.into(), description: format!("Preserve original {name}"), parent: None, body: ElementBody::new(kind, layer).unwrap() });
|
||||
}
|
||||
p.relations.insert(id(20), Relation { id:id(20), alias:"original_allocation".into(), name:"Original responsibility".into(), description:"Preserve allocation engineering rationale 雨".into(), body:RelationBody::Allocation { source:id(10), target:id(11) } });
|
||||
if layer == Layer::Oa {
|
||||
for n in [12,13] {
|
||||
p.elements.insert(id(n), Element { id:id(n), alias:format!("role_{n}"), name:format!("Responsibility role {n}"), description:"Preserve role".into(), parent:None, body:ElementBody::new(Kind::Role, layer).unwrap() });
|
||||
for (r, kind, source, target) in [(n+10,RelationKind::RoleAssignment,id(n),id(11)),(n+20,RelationKind::RoleRealization,id(10),id(n))] {
|
||||
p.relations.insert(id(r), Relation { id:id(r), alias:format!("responsibility_{r}"), name:format!("Role link {r}"), description:format!("Retain role link metadata {r}"), body:RelationBody::new(kind,source,target) });
|
||||
}
|
||||
}
|
||||
}
|
||||
for (index, owner) in [id(20),id(22),id(23)].into_iter().filter(|owner| p.relations.contains_key(owner)).enumerate() {
|
||||
let key=id(50+index as u128);
|
||||
p.artifact_links.insert(key, ArtifactLink { id:key, alias:format!("evidence_{index}"), name:format!("Responsibility evidence {index}"), description:"Keep owner identity and evidence metadata".into(), owner, relation:ArtifactRelation::References, uri:format!("https://example.invalid/evidence/{index}"), revision:Some("review-7".into()), fragment:Some("assignment".into()) });
|
||||
}
|
||||
p.diagrams.insert(id(2), Diagram { id:id(2), alias:"allocation_view".into(), name:format!("{layer:?} allocated work"), description:"Native child-work fixture".into(), layer, projection:ProjectionKind::Allocation, routes:vec![], view_options:Default::default(), nodes:BTreeMap::new() });
|
||||
for (n,element,x,y,collapsed) in [(40,id(10),60.,60.,false),(41,id(11),84.,124.,true)] {
|
||||
p.diagrams.get_mut(&id(2)).unwrap().nodes.insert(id(n), NodePlacement { id:id(n), alias:format!("seed_view_{n}"), element, position:Position{x,y}, size:Size{width:224.,height:94.}, collapsed, port_anchors:vec![] });
|
||||
}
|
||||
execute_batch(&p,&[]).unwrap()
|
||||
}
|
||||
fn bundle(p:&Project)->CompiledSourceProject {
|
||||
let source=language::format(p).replacen("{\n","{\n include \"rationale.arcadia\";\n",1);
|
||||
let raw=SourceBundle { entry:"project.arcadia".into(), files:BTreeMap::from([("project.arcadia".into(),source),("rationale.arcadia".into(),"// Preserve included note bytes: responsibility 雨.\r\n".into())]) };
|
||||
let result=compile_bundle(&raw,ProjectLimits::default()).unwrap();
|
||||
assert_eq!(result.bundle,raw); assert_eq!(&result.decoded.project,p); result
|
||||
}
|
||||
fn load(entry:&Path)->CompiledSourceProject {
|
||||
let raw=source_files::load_project(entry,ProjectLimits::default()).unwrap().reachable_bundle();
|
||||
let result=compile_bundle(&raw,ProjectLimits::default()).unwrap();
|
||||
assert_eq!(result.bundle,raw,"No source normalization permitted");
|
||||
execute_batch(&result.decoded.project,&[]).unwrap(); result
|
||||
}
|
||||
fn advance(base:&mut CompiledSourceProject,commands:Vec<Command>) {
|
||||
let expected=execute_batch(&base.decoded.project,&commands).unwrap();
|
||||
*base=update_bundle(base,&expected,ProjectLimits::default()).unwrap();
|
||||
}
|
||||
#[derive(Clone, Copy)]
|
||||
struct Keys { children:[Id;3], nodes:[Id;3], allocation:Id }
|
||||
fn expected(layer:Layer,stage:usize,k:Keys)->CompiledSourceProject {
|
||||
assert!(stage<=3);
|
||||
let mut base=bundle(&fixture(layer));
|
||||
for step in 0..stage {
|
||||
let p=&base.decoded.project;
|
||||
let key=k.children[step];
|
||||
let name=format!("New {}",work(layer).label().to_lowercase());
|
||||
let element=Element { id:key, alias:p.fresh_alias(&name,"element",key), name, description:String::new(), parent:Some(if step==2 { k.children[0] } else { id(11) }), body:ElementBody::new(work(layer),layer).unwrap() };
|
||||
let mut commands=vec![Command::CreateElement(element.clone())];
|
||||
if step==1 {
|
||||
commands.push(Command::CreateRelation(Relation { id:k.allocation, alias:p.fresh_alias("Performs","relation",k.allocation), name:"Performs".into(), description:String::new(), body:RelationBody::Allocation { source:id(10),target:key } }));
|
||||
} else {
|
||||
for original in p.relations.values().filter(|r| [id(20),id(22),id(23)].contains(&r.id)) {
|
||||
let mut r=original.clone();
|
||||
match &mut r.body { RelationBody::Allocation{target,..}|RelationBody::RoleAssignment{target,..}=>*target=key,_=>unreachable!() }
|
||||
commands.push(Command::EditRelation(r.id,r));
|
||||
}
|
||||
}
|
||||
if step==0 {
|
||||
let mut node=p.diagrams[&id(2)].nodes[&id(41)].clone(); node.collapsed=false;
|
||||
commands.push(Command::EditNode{diagram:id(2),original:node.id,node});
|
||||
}
|
||||
let mut with_element=p.clone(); with_element.elements.insert(key,element);
|
||||
let (x,y)=[(108.,188.),(108.,298.),(132.,252.)][step];
|
||||
commands.push(Command::CreateNode { diagram:id(2), node:NodePlacement { id:k.nodes[step], alias:with_element.fresh_alias(&format!("view_{}",with_element.named(key)),"node",k.nodes[step]), element:key,position:Position{x,y},size:Size{width:224.,height:94.},collapsed:false,port_anchors:vec![] } });
|
||||
if step==2 {
|
||||
// Grandchild expands first child to bottom370; sibling follows at394
|
||||
// (24px reflow gap), independently predicted for this fixed fixture.
|
||||
let mut sibling=p.diagrams[&id(2)].nodes[&k.nodes[1]].clone(); sibling.position.y=394.;
|
||||
commands.push(Command::EditNode{diagram:id(2),original:sibling.id,node:sibling});
|
||||
}
|
||||
advance(&mut base,commands);
|
||||
}
|
||||
base
|
||||
}
|
||||
fn only(ids:Vec<Id>,role:&str)->Id { assert_eq!(ids.len(),1,"Exactly one {role}"); ids[0] }
|
||||
fn admit(actual:&Project,layer:Layer,stage:usize)->Keys {
|
||||
let mut k=Keys{children:[Id::nil();3],nodes:[Id::nil();3],allocation:Id::nil()};
|
||||
if stage==0 { return k; }
|
||||
let target=actual.relations[&id(20)].target();
|
||||
k.children[0]=if stage==3 { actual.elements[&target].parent.unwrap() } else { target };
|
||||
if stage>=2 {
|
||||
k.children[1]=only(actual.elements.values().filter(|e|e.parent==Some(id(11))&&e.id!=k.children[0]&&e.kind()==work(layer)).map(|e|e.id).collect(),"second child");
|
||||
k.allocation=only(actual.relations.values().filter(|r|r.kind()==RelationKind::Allocation&&r.source()==id(10)&&r.target()==k.children[1]).map(|r|r.id).collect(),"new sibling allocation");
|
||||
}
|
||||
if stage==3 { k.children[2]=target; }
|
||||
for step in 0..stage {
|
||||
let e=&actual.elements[&k.children[step]];
|
||||
assert_eq!(e.kind(),work(layer)); assert_eq!(e.layer(),layer);
|
||||
assert_eq!(e.parent,Some(if step==2{k.children[0]}else{id(11)}));
|
||||
k.nodes[step]=only(actual.diagrams[&id(2)].nodes.values().filter(|n|n.element==e.id).map(|n|n.id).collect(),"child placement");
|
||||
}
|
||||
let seed=fixture(layer); let mut used=BTreeSet::new();
|
||||
for key in k.children[..stage].iter().chain(&k.nodes[..stage]).copied().chain((stage>=2).then_some(k.allocation)) {
|
||||
assert_eq!(key.get_version_num(),4,"Native fresh UUIDv4 required");
|
||||
assert!(!seed.contains(key)&&used.insert(key),"Fresh distinct role identity required");
|
||||
}
|
||||
k
|
||||
}
|
||||
fn check(actual:&CompiledSourceProject,layer:Layer,stage:usize) {
|
||||
let prediction=expected(layer,stage,admit(&actual.decoded.project,layer,stage));
|
||||
assert_eq!(actual.decoded.project,prediction.decoded.project,"Complete typed model mismatch, including placements and references");
|
||||
assert_eq!(actual.bundle,prediction.bundle,"Exact source mismatch, including included note bytes");
|
||||
}
|
||||
fn history(actual:&CompiledSourceProject,layer:Layer,stage:usize,first:&CompiledSourceProject,second:Option<&CompiledSourceProject>) {
|
||||
check(first,layer,1);
|
||||
let first_keys=admit(&first.decoded.project,layer,1);
|
||||
let current_keys=admit(&actual.decoded.project,layer,stage);
|
||||
assert_eq!(current_keys.children[0],first_keys.children[0],"First child identity must match original first-commit carrier");
|
||||
assert_eq!(current_keys.nodes[0],first_keys.nodes[0],"First placement identity must match original first-commit carrier");
|
||||
if stage==3 {
|
||||
let second=second.expect("Original second-commit carrier required");
|
||||
check(second,layer,2);
|
||||
history(second,layer,2,first,None);
|
||||
let second_keys=admit(&second.decoded.project,layer,2);
|
||||
assert_eq!(current_keys.children[1],second_keys.children[1],"Sibling identity must persist");
|
||||
assert_eq!(current_keys.nodes[1],second_keys.nodes[1],"Sibling placement identity must persist");
|
||||
assert_eq!(current_keys.allocation,second_keys.allocation,"Sibling allocation identity must persist");
|
||||
}
|
||||
}
|
||||
fn write_bundle(dir:&Path,raw:&SourceBundle) {
|
||||
assert!(!dir.exists(),"Use a new evidence directory"); fs::create_dir_all(dir).unwrap();
|
||||
for (name,bytes) in &raw.files { let path=dir.join(name); if let Some(parent)=path.parent(){fs::create_dir_all(parent).unwrap();} fs::write(path,bytes).unwrap(); }
|
||||
}
|
||||
fn main() {
|
||||
assert_eq!(SCHEMA_VERSION,7);
|
||||
let args:Vec<_>=std::env::args().collect();
|
||||
match args.get(1).map(String::as_str) {
|
||||
Some("generate")=>{assert_eq!(args.len(),3);let dir=Path::new(&args[2]);assert!(!dir.exists()); for (name,layer) in [("oa",Layer::Oa),("sa",Layer::Sa)] {write_bundle(&dir.join(name),&bundle(&fixture(layer)).bundle);} println!("PASS deterministic OA/SA native fixtures generated");}
|
||||
Some("check")=>{
|
||||
let layer=match args[2].as_str(){"oa"=>Layer::Oa,"sa"=>Layer::Sa,_=>panic!("oa|sa")};
|
||||
let stage=args[3].parse::<usize>().unwrap();assert!(stage<=3);
|
||||
assert_eq!(args.len(),5+stage.saturating_sub(1),"Stages2/3 require original stage1/stage2 checkpoint directories");
|
||||
let actual=load(Path::new(&args[4]));check(&actual,layer,stage);
|
||||
if stage>=2 {
|
||||
let first=load(&Path::new(&args[5]).join("project.arcadia"));
|
||||
let second=if stage==3 {Some(load(&Path::new(&args[6]).join("project.arcadia")))} else {None};
|
||||
history(&actual,layer,stage,&first,second.as_ref());
|
||||
}
|
||||
println!("PASS complete model, exact source and pinned history stage {stage}");
|
||||
}
|
||||
Some("snapshot")=>{assert_eq!(args.len(),4);let actual=load(Path::new(&args[2]));write_bundle(Path::new(&args[3]),&actual.bundle);println!("PASS immutable source checkpoint");}
|
||||
Some("compare")=>{assert_eq!(args.len(),4);let actual=load(Path::new(&args[2]));let checkpoint=load(&Path::new(&args[3]).join(&actual.bundle.entry));assert_eq!(actual.bundle,checkpoint.bundle,"Saved reachable source bytes changed");assert_eq!(actual.decoded.project,checkpoint.decoded.project);println!("PASS exact checkpoint bytes and model");}
|
||||
Some("self-check")=>{for layer in [Layer::Oa,Layer::Sa]{let keys=Keys{children:std::array::from_fn(|_|Id::new_v4()),nodes:std::array::from_fn(|_|Id::new_v4()),allocation:Id::new_v4()};for stage in 0..=3 {let candidate=expected(layer,stage,keys);check(&candidate,layer,stage);if stage>=2 {history(&candidate,layer,stage,&expected(layer,1,keys),(stage==3).then(||expected(layer,2,keys)).as_ref());}let mut altered=candidate.decoded.project.clone();altered.elements.get_mut(&id(11)).unwrap().description="Unrequested mutation".into();assert_ne!(expected(layer,stage,admit(&altered,layer,stage)).decoded.project,altered);}}println!("PASS oracle fixed points and unrelated-mutation discriminator");}
|
||||
_=>panic!("generate DIR | check oa|sa STAGE ENTRY [STAGE1DIR] [STAGE2DIR] | snapshot ENTRY DIR | compare ENTRY DIR | self-check"),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
# Analysis context experiment
|
||||
|
||||
This standalone Rust program compares explicit baseline/override studies (B) with complete independent case bindings (D). It is an isolated experiment, **not Studio syntax or a production model implementation**. Its expression language is deliberately limited to integer magnitudes in milliseconds, addition/sum, comparison, missing inputs and pure reusable functions.
|
||||
|
||||
The source owns the fixture generator, parser, explicit context resolver, dependency-cycle check, evaluator, frozen-snapshot operation, provenance observations and line-diff counter. All comparisons use the same 20 fixed inputs, 2 varying inputs and 20 named cases. The formula/check variants are either inline or factored into reusable pure definitions.
|
||||
|
||||
From the repository root, using the installed local toolchain:
|
||||
|
||||
```powershell
|
||||
. .local/toolchain/env.ps1
|
||||
New-Item -ItemType Directory -Force .local/analysis-context-20260911 | Out-Null
|
||||
rustc --edition=2024 -D warnings -C linker=clang.exe harness/experiments/analysis-context-20260911/main.rs -o .local/analysis-context-20260911/analysis-context.exe
|
||||
if ($LASTEXITCODE -ne 0) { throw 'Experiment compilation failed' }
|
||||
& .local/analysis-context-20260911/analysis-context.exe harness/experiments/analysis-context-20260911/generated
|
||||
if ($LASTEXITCODE -ne 0) { throw 'Experiment assertions failed' }
|
||||
```
|
||||
|
||||
The program overwrites its named generated fixtures/results in the output directory. It does not invoke Cargo, modify `target`, or import Studio modules. `EVIDENCE.json` was recorded separately after the verified run and contains exact compiler, program/executable/result hashes and every generated source fixture hash. Rerunning after editing the program requires refreshing that evidence rather than trusting the old manifest.
|
||||
|
||||
- [Results](generated/RESULTS.md): executed counts and assertions.
|
||||
- [Evidence](generated/EVIDENCE.json): final run identity and 29 fixture hashes.
|
||||
- [B with factored calculations](generated/B-factored.study) and [D equivalent](generated/D-factored.study).
|
||||
- [B case provenance](generated/B-factored-provenance.md).
|
||||
- [Frozen B case](generated/B-factored-frozen-case20.study): complete independent snapshot, copied definitions and explicit captured row origins.
|
||||
- [Interpretation and limitations](../../release-analysis-context-experiment-20260911.md).
|
||||
|
||||
The small dialect has explicit `study B/D revision`, a shared property catalog, optional pure `function` declarations, an explicit named `baseline` for B, and named cases declaring either `base engineering` or `independent`. `missing` is an entire binding hole. Removing a B override resolves the baseline; removing a required D row rejects. Function parameters cannot capture project properties. No case-to-case inheritance or implicit selected/default case exists.
|
||||
|
||||
Generated studies do not have OA/SA subjects, UUID identity, quantity kinds, persistent source editing, JSON/REST, library import resolution or native UI integration. The experiment compares context semantics and the actual emitted text size/edit count, not human completion time or end-to-end product conformance.
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
study B revision 1
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
function budget_formula(result) = le(result, 22000)
|
||||
function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
baseline engineering revision 1
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case01 base engineering
|
||||
end
|
||||
case case02 base engineering
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 base engineering
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 base engineering
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 base engineering
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 base engineering
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 base engineering
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 base engineering
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 base engineering
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 base engineering
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 base engineering
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 base engineering
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 base engineering
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 base engineering
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 base engineering
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 base engineering
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 base engineering
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 base engineering
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 base engineering
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 base engineering
|
||||
varying02 = 1475
|
||||
end
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
study B revision 1
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
function budget_formula(result) = le(result, 22000)
|
||||
function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
baseline engineering revision 1
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case01 base engineering
|
||||
end
|
||||
case case02 base engineering
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 base engineering
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 base engineering
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 base engineering
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 base engineering
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 base engineering
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 base engineering
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 base engineering
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 base engineering
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 base engineering
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 base engineering
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 base engineering
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 base engineering
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 base engineering
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 base engineering
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 base engineering
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 base engineering
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 base engineering
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 base engineering
|
||||
varying01 = total
|
||||
varying02 = 1475
|
||||
end
|
||||
@@ -0,0 +1,108 @@
|
||||
study B revision 2
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
function budget_formula(result) = le(result, 22000)
|
||||
function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
baseline engineering revision 2
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case01 base engineering
|
||||
end
|
||||
case case02 base engineering
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 base engineering
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 base engineering
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 base engineering
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 base engineering
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 base engineering
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 base engineering
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 base engineering
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 base engineering
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 base engineering
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 base engineering
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 base engineering
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 base engineering
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 base engineering
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 base engineering
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 base engineering
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 base engineering
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 base engineering
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 base engineering
|
||||
varying01 = 1700
|
||||
varying02 = 1475
|
||||
end
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
study D revision 1
|
||||
origin B:case20:document1:engineering@1
|
||||
captured budget = baseline:engineering@1 via case20
|
||||
captured fixed01 = baseline:engineering@1 via case20
|
||||
captured fixed02 = baseline:engineering@1 via case20
|
||||
captured fixed03 = baseline:engineering@1 via case20
|
||||
captured fixed04 = baseline:engineering@1 via case20
|
||||
captured fixed05 = baseline:engineering@1 via case20
|
||||
captured fixed06 = baseline:engineering@1 via case20
|
||||
captured fixed07 = baseline:engineering@1 via case20
|
||||
captured fixed08 = baseline:engineering@1 via case20
|
||||
captured fixed09 = baseline:engineering@1 via case20
|
||||
captured fixed10 = baseline:engineering@1 via case20
|
||||
captured fixed11 = baseline:engineering@1 via case20
|
||||
captured fixed12 = baseline:engineering@1 via case20
|
||||
captured fixed13 = baseline:engineering@1 via case20
|
||||
captured fixed14 = baseline:engineering@1 via case20
|
||||
captured fixed15 = baseline:engineering@1 via case20
|
||||
captured fixed16 = baseline:engineering@1 via case20
|
||||
captured fixed17 = baseline:engineering@1 via case20
|
||||
captured fixed18 = baseline:engineering@1 via case20
|
||||
captured fixed19 = baseline:engineering@1 via case20
|
||||
captured fixed20 = baseline:engineering@1 via case20
|
||||
captured total = baseline:engineering@1 via case20
|
||||
captured varying01 = case:case20
|
||||
captured varying02 = case:case20
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
function budget_formula(result) = le(result, 22000)
|
||||
function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
case case20 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1700
|
||||
varying02 = 1475
|
||||
end
|
||||
@@ -0,0 +1,108 @@
|
||||
study B revision 1
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
function budget_formula(result) = le(result, 22000)
|
||||
function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
baseline engineering revision 1
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case01 base engineering
|
||||
end
|
||||
case case02 base engineering
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 base engineering
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 base engineering
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 base engineering
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 base engineering
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 base engineering
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 base engineering
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 base engineering
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 base engineering
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 base engineering
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 base engineering
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 base engineering
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 base engineering
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 base engineering
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 base engineering
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 base engineering
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 base engineering
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 base engineering
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 base engineering
|
||||
varying01 = missing
|
||||
varying02 = 1475
|
||||
end
|
||||
@@ -0,0 +1,31 @@
|
||||
# B-factored: case20 evaluation
|
||||
|
||||
Value: Bool(false)
|
||||
Document revision: 1
|
||||
|
||||
- budget: baseline:engineering@1 via case20
|
||||
- fixed01: baseline:engineering@1 via case20
|
||||
- fixed02: baseline:engineering@1 via case20
|
||||
- fixed03: baseline:engineering@1 via case20
|
||||
- fixed04: baseline:engineering@1 via case20
|
||||
- fixed05: baseline:engineering@1 via case20
|
||||
- fixed06: baseline:engineering@1 via case20
|
||||
- fixed07: baseline:engineering@1 via case20
|
||||
- fixed08: baseline:engineering@1 via case20
|
||||
- fixed09: baseline:engineering@1 via case20
|
||||
- fixed10: baseline:engineering@1 via case20
|
||||
- fixed11: baseline:engineering@1 via case20
|
||||
- fixed12: baseline:engineering@1 via case20
|
||||
- fixed13: baseline:engineering@1 via case20
|
||||
- fixed14: baseline:engineering@1 via case20
|
||||
- fixed15: baseline:engineering@1 via case20
|
||||
- fixed16: baseline:engineering@1 via case20
|
||||
- fixed17: baseline:engineering@1 via case20
|
||||
- fixed18: baseline:engineering@1 via case20
|
||||
- fixed19: baseline:engineering@1 via case20
|
||||
- fixed20: baseline:engineering@1 via case20
|
||||
- total: baseline:engineering@1 via case20
|
||||
- varying01: case:case20
|
||||
- varying02: case:case20
|
||||
|
||||
Rejected cycle: case20: cycle: budget -> total -> varying01 -> total
|
||||
@@ -0,0 +1,108 @@
|
||||
study B revision 2
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
function budget_formula(result) = le(result, 22500)
|
||||
function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
baseline engineering revision 1
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case01 base engineering
|
||||
end
|
||||
case case02 base engineering
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 base engineering
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 base engineering
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 base engineering
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 base engineering
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 base engineering
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 base engineering
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 base engineering
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 base engineering
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 base engineering
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 base engineering
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 base engineering
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 base engineering
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 base engineering
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 base engineering
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 base engineering
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 base engineering
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 base engineering
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 base engineering
|
||||
varying01 = 1700
|
||||
varying02 = 1475
|
||||
end
|
||||
@@ -0,0 +1,108 @@
|
||||
study B revision 1
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
function budget_formula(result) = le(result, 22000)
|
||||
function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
baseline engineering revision 1
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case01 base engineering
|
||||
end
|
||||
case case02 base engineering
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 base engineering
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 base engineering
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 base engineering
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 base engineering
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 base engineering
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 base engineering
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 base engineering
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 base engineering
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 base engineering
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 base engineering
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 base engineering
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 base engineering
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 base engineering
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 base engineering
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 base engineering
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 base engineering
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 base engineering
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 base engineering
|
||||
varying01 = 1700
|
||||
varying02 = 1475
|
||||
end
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
study B revision 1
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
baseline engineering revision 1
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case01 base engineering
|
||||
end
|
||||
case case02 base engineering
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 base engineering
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 base engineering
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 base engineering
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 base engineering
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 base engineering
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 base engineering
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 base engineering
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 base engineering
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 base engineering
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 base engineering
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 base engineering
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 base engineering
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 base engineering
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 base engineering
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 base engineering
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 base engineering
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 base engineering
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 base engineering
|
||||
varying02 = 1475
|
||||
end
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
study B revision 1
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
baseline engineering revision 1
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case01 base engineering
|
||||
end
|
||||
case case02 base engineering
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 base engineering
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 base engineering
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 base engineering
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 base engineering
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 base engineering
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 base engineering
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 base engineering
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 base engineering
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 base engineering
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 base engineering
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 base engineering
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 base engineering
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 base engineering
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 base engineering
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 base engineering
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 base engineering
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 base engineering
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 base engineering
|
||||
varying01 = total
|
||||
varying02 = 1475
|
||||
end
|
||||
@@ -0,0 +1,106 @@
|
||||
study B revision 2
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
baseline engineering revision 2
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case01 base engineering
|
||||
end
|
||||
case case02 base engineering
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 base engineering
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 base engineering
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 base engineering
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 base engineering
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 base engineering
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 base engineering
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 base engineering
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 base engineering
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 base engineering
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 base engineering
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 base engineering
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 base engineering
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 base engineering
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 base engineering
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 base engineering
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 base engineering
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 base engineering
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 base engineering
|
||||
varying01 = 1700
|
||||
varying02 = 1475
|
||||
end
|
||||
@@ -0,0 +1,53 @@
|
||||
study D revision 1
|
||||
origin B:case20:document1:engineering@1
|
||||
captured budget = baseline:engineering@1 via case20
|
||||
captured fixed01 = baseline:engineering@1 via case20
|
||||
captured fixed02 = baseline:engineering@1 via case20
|
||||
captured fixed03 = baseline:engineering@1 via case20
|
||||
captured fixed04 = baseline:engineering@1 via case20
|
||||
captured fixed05 = baseline:engineering@1 via case20
|
||||
captured fixed06 = baseline:engineering@1 via case20
|
||||
captured fixed07 = baseline:engineering@1 via case20
|
||||
captured fixed08 = baseline:engineering@1 via case20
|
||||
captured fixed09 = baseline:engineering@1 via case20
|
||||
captured fixed10 = baseline:engineering@1 via case20
|
||||
captured fixed11 = baseline:engineering@1 via case20
|
||||
captured fixed12 = baseline:engineering@1 via case20
|
||||
captured fixed13 = baseline:engineering@1 via case20
|
||||
captured fixed14 = baseline:engineering@1 via case20
|
||||
captured fixed15 = baseline:engineering@1 via case20
|
||||
captured fixed16 = baseline:engineering@1 via case20
|
||||
captured fixed17 = baseline:engineering@1 via case20
|
||||
captured fixed18 = baseline:engineering@1 via case20
|
||||
captured fixed19 = baseline:engineering@1 via case20
|
||||
captured fixed20 = baseline:engineering@1 via case20
|
||||
captured total = baseline:engineering@1 via case20
|
||||
captured varying01 = case:case20
|
||||
captured varying02 = case:case20
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
case case20 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1700
|
||||
varying02 = 1475
|
||||
end
|
||||
@@ -0,0 +1,106 @@
|
||||
study B revision 1
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
baseline engineering revision 1
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case01 base engineering
|
||||
end
|
||||
case case02 base engineering
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 base engineering
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 base engineering
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 base engineering
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 base engineering
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 base engineering
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 base engineering
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 base engineering
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 base engineering
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 base engineering
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 base engineering
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 base engineering
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 base engineering
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 base engineering
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 base engineering
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 base engineering
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 base engineering
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 base engineering
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 base engineering
|
||||
varying01 = missing
|
||||
varying02 = 1475
|
||||
end
|
||||
@@ -0,0 +1,31 @@
|
||||
# B-inline: case20 evaluation
|
||||
|
||||
Value: Bool(false)
|
||||
Document revision: 1
|
||||
|
||||
- budget: baseline:engineering@1 via case20
|
||||
- fixed01: baseline:engineering@1 via case20
|
||||
- fixed02: baseline:engineering@1 via case20
|
||||
- fixed03: baseline:engineering@1 via case20
|
||||
- fixed04: baseline:engineering@1 via case20
|
||||
- fixed05: baseline:engineering@1 via case20
|
||||
- fixed06: baseline:engineering@1 via case20
|
||||
- fixed07: baseline:engineering@1 via case20
|
||||
- fixed08: baseline:engineering@1 via case20
|
||||
- fixed09: baseline:engineering@1 via case20
|
||||
- fixed10: baseline:engineering@1 via case20
|
||||
- fixed11: baseline:engineering@1 via case20
|
||||
- fixed12: baseline:engineering@1 via case20
|
||||
- fixed13: baseline:engineering@1 via case20
|
||||
- fixed14: baseline:engineering@1 via case20
|
||||
- fixed15: baseline:engineering@1 via case20
|
||||
- fixed16: baseline:engineering@1 via case20
|
||||
- fixed17: baseline:engineering@1 via case20
|
||||
- fixed18: baseline:engineering@1 via case20
|
||||
- fixed19: baseline:engineering@1 via case20
|
||||
- fixed20: baseline:engineering@1 via case20
|
||||
- total: baseline:engineering@1 via case20
|
||||
- varying01: case:case20
|
||||
- varying02: case:case20
|
||||
|
||||
Rejected cycle: case20: cycle: budget -> total -> varying01 -> total
|
||||
@@ -0,0 +1,106 @@
|
||||
study B revision 2
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
baseline engineering revision 2
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case01 base engineering
|
||||
end
|
||||
case case02 base engineering
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 base engineering
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 base engineering
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 base engineering
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 base engineering
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 base engineering
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 base engineering
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 base engineering
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 base engineering
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 base engineering
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 base engineering
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 base engineering
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 base engineering
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 base engineering
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 base engineering
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 base engineering
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 base engineering
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 base engineering
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 base engineering
|
||||
varying01 = 1700
|
||||
varying02 = 1475
|
||||
end
|
||||
@@ -0,0 +1,106 @@
|
||||
study B revision 1
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
baseline engineering revision 1
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case01 base engineering
|
||||
end
|
||||
case case02 base engineering
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 base engineering
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 base engineering
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 base engineering
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 base engineering
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 base engineering
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 base engineering
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 base engineering
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 base engineering
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 base engineering
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 base engineering
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 base engineering
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 base engineering
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 base engineering
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 base engineering
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 base engineering
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 base engineering
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 base engineering
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 base engineering
|
||||
varying01 = 1700
|
||||
varying02 = 1475
|
||||
end
|
||||
+524
@@ -0,0 +1,524 @@
|
||||
study D revision 1
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
function budget_formula(result) = le(result, 22000)
|
||||
function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
case case01 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case02 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = total
|
||||
varying02 = 1475
|
||||
end
|
||||
+523
@@ -0,0 +1,523 @@
|
||||
study D revision 1
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
function budget_formula(result) = le(result, 22000)
|
||||
function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
case case01 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case02 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying02 = 1475
|
||||
end
|
||||
@@ -0,0 +1,524 @@
|
||||
study D revision 2
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
function budget_formula(result) = le(result, 22000)
|
||||
function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
case case01 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case02 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1700
|
||||
varying02 = 1475
|
||||
end
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
study D revision 1
|
||||
origin D:case20:document1
|
||||
captured budget = case:case20
|
||||
captured fixed01 = case:case20
|
||||
captured fixed02 = case:case20
|
||||
captured fixed03 = case:case20
|
||||
captured fixed04 = case:case20
|
||||
captured fixed05 = case:case20
|
||||
captured fixed06 = case:case20
|
||||
captured fixed07 = case:case20
|
||||
captured fixed08 = case:case20
|
||||
captured fixed09 = case:case20
|
||||
captured fixed10 = case:case20
|
||||
captured fixed11 = case:case20
|
||||
captured fixed12 = case:case20
|
||||
captured fixed13 = case:case20
|
||||
captured fixed14 = case:case20
|
||||
captured fixed15 = case:case20
|
||||
captured fixed16 = case:case20
|
||||
captured fixed17 = case:case20
|
||||
captured fixed18 = case:case20
|
||||
captured fixed19 = case:case20
|
||||
captured fixed20 = case:case20
|
||||
captured total = case:case20
|
||||
captured varying01 = case:case20
|
||||
captured varying02 = case:case20
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
function budget_formula(result) = le(result, 22000)
|
||||
function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
case case20 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1700
|
||||
varying02 = 1475
|
||||
end
|
||||
@@ -0,0 +1,524 @@
|
||||
study D revision 1
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
function budget_formula(result) = le(result, 22000)
|
||||
function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
case case01 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case02 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = missing
|
||||
varying02 = 1475
|
||||
end
|
||||
@@ -0,0 +1,31 @@
|
||||
# D-factored: case20 evaluation
|
||||
|
||||
Value: Bool(false)
|
||||
Document revision: 1
|
||||
|
||||
- budget: case:case20
|
||||
- fixed01: case:case20
|
||||
- fixed02: case:case20
|
||||
- fixed03: case:case20
|
||||
- fixed04: case:case20
|
||||
- fixed05: case:case20
|
||||
- fixed06: case:case20
|
||||
- fixed07: case:case20
|
||||
- fixed08: case:case20
|
||||
- fixed09: case:case20
|
||||
- fixed10: case:case20
|
||||
- fixed11: case:case20
|
||||
- fixed12: case:case20
|
||||
- fixed13: case:case20
|
||||
- fixed14: case:case20
|
||||
- fixed15: case:case20
|
||||
- fixed16: case:case20
|
||||
- fixed17: case:case20
|
||||
- fixed18: case:case20
|
||||
- fixed19: case:case20
|
||||
- fixed20: case:case20
|
||||
- total: case:case20
|
||||
- varying01: case:case20
|
||||
- varying02: case:case20
|
||||
|
||||
Rejected cycle: case20: cycle: budget -> total -> varying01 -> total
|
||||
@@ -0,0 +1,524 @@
|
||||
study D revision 2
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
function budget_formula(result) = le(result, 22500)
|
||||
function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
case case01 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case02 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1700
|
||||
varying02 = 1475
|
||||
end
|
||||
@@ -0,0 +1,524 @@
|
||||
study D revision 1
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
function budget_formula(result) = le(result, 22000)
|
||||
function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
case case01 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case02 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 independent
|
||||
budget = budget_formula(total)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1700
|
||||
varying02 = 1475
|
||||
end
|
||||
+522
@@ -0,0 +1,522 @@
|
||||
study D revision 1
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
case case01 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case02 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = total
|
||||
varying02 = 1475
|
||||
end
|
||||
+521
@@ -0,0 +1,521 @@
|
||||
study D revision 1
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
case case01 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case02 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying02 = 1475
|
||||
end
|
||||
@@ -0,0 +1,522 @@
|
||||
study D revision 2
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
case case01 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case02 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1700
|
||||
varying02 = 1475
|
||||
end
|
||||
+522
@@ -0,0 +1,522 @@
|
||||
study D revision 2
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
case case01 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1200
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case02 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1700
|
||||
varying02 = 1475
|
||||
end
|
||||
@@ -0,0 +1,53 @@
|
||||
study D revision 1
|
||||
origin D:case20:document1
|
||||
captured budget = case:case20
|
||||
captured fixed01 = case:case20
|
||||
captured fixed02 = case:case20
|
||||
captured fixed03 = case:case20
|
||||
captured fixed04 = case:case20
|
||||
captured fixed05 = case:case20
|
||||
captured fixed06 = case:case20
|
||||
captured fixed07 = case:case20
|
||||
captured fixed08 = case:case20
|
||||
captured fixed09 = case:case20
|
||||
captured fixed10 = case:case20
|
||||
captured fixed11 = case:case20
|
||||
captured fixed12 = case:case20
|
||||
captured fixed13 = case:case20
|
||||
captured fixed14 = case:case20
|
||||
captured fixed15 = case:case20
|
||||
captured fixed16 = case:case20
|
||||
captured fixed17 = case:case20
|
||||
captured fixed18 = case:case20
|
||||
captured fixed19 = case:case20
|
||||
captured fixed20 = case:case20
|
||||
captured total = case:case20
|
||||
captured varying01 = case:case20
|
||||
captured varying02 = case:case20
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
case case20 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1700
|
||||
varying02 = 1475
|
||||
end
|
||||
@@ -0,0 +1,522 @@
|
||||
study D revision 1
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
case case01 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case02 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = missing
|
||||
varying02 = 1475
|
||||
end
|
||||
@@ -0,0 +1,31 @@
|
||||
# D-inline: case20 evaluation
|
||||
|
||||
Value: Bool(false)
|
||||
Document revision: 1
|
||||
|
||||
- budget: case:case20
|
||||
- fixed01: case:case20
|
||||
- fixed02: case:case20
|
||||
- fixed03: case:case20
|
||||
- fixed04: case:case20
|
||||
- fixed05: case:case20
|
||||
- fixed06: case:case20
|
||||
- fixed07: case:case20
|
||||
- fixed08: case:case20
|
||||
- fixed09: case:case20
|
||||
- fixed10: case:case20
|
||||
- fixed11: case:case20
|
||||
- fixed12: case:case20
|
||||
- fixed13: case:case20
|
||||
- fixed14: case:case20
|
||||
- fixed15: case:case20
|
||||
- fixed16: case:case20
|
||||
- fixed17: case:case20
|
||||
- fixed18: case:case20
|
||||
- fixed19: case:case20
|
||||
- fixed20: case:case20
|
||||
- total: case:case20
|
||||
- varying01: case:case20
|
||||
- varying02: case:case20
|
||||
|
||||
Rejected cycle: case20: cycle: budget -> total -> varying01 -> total
|
||||
@@ -0,0 +1,522 @@
|
||||
study D revision 2
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
case case01 independent
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case02 independent
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 independent
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 independent
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 independent
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 independent
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 independent
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 independent
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 independent
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 independent
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 independent
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 independent
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 independent
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 independent
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 independent
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 independent
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 independent
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 independent
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 independent
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 independent
|
||||
budget = le(total, 22500)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1700
|
||||
varying02 = 1475
|
||||
end
|
||||
@@ -0,0 +1,522 @@
|
||||
study D revision 1
|
||||
properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
|
||||
case case01 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 750
|
||||
varying02 = 1000
|
||||
end
|
||||
case case02 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 800
|
||||
varying02 = 1025
|
||||
end
|
||||
case case03 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 850
|
||||
varying02 = 1050
|
||||
end
|
||||
case case04 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 900
|
||||
varying02 = 1075
|
||||
end
|
||||
case case05 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 950
|
||||
varying02 = 1100
|
||||
end
|
||||
case case06 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1000
|
||||
varying02 = 1125
|
||||
end
|
||||
case case07 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1050
|
||||
varying02 = 1150
|
||||
end
|
||||
case case08 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1100
|
||||
varying02 = 1175
|
||||
end
|
||||
case case09 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1150
|
||||
varying02 = 1200
|
||||
end
|
||||
case case10 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1200
|
||||
varying02 = 1225
|
||||
end
|
||||
case case11 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1250
|
||||
varying02 = 1250
|
||||
end
|
||||
case case12 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1300
|
||||
varying02 = 1275
|
||||
end
|
||||
case case13 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1350
|
||||
varying02 = 1300
|
||||
end
|
||||
case case14 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1400
|
||||
varying02 = 1325
|
||||
end
|
||||
case case15 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1450
|
||||
varying02 = 1350
|
||||
end
|
||||
case case16 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1500
|
||||
varying02 = 1375
|
||||
end
|
||||
case case17 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1550
|
||||
varying02 = 1400
|
||||
end
|
||||
case case18 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1600
|
||||
varying02 = 1425
|
||||
end
|
||||
case case19 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1650
|
||||
varying02 = 1450
|
||||
end
|
||||
case case20 independent
|
||||
budget = le(total, 22000)
|
||||
fixed01 = 1000
|
||||
fixed02 = 1000
|
||||
fixed03 = 1000
|
||||
fixed04 = 1000
|
||||
fixed05 = 1000
|
||||
fixed06 = 1000
|
||||
fixed07 = 1000
|
||||
fixed08 = 1000
|
||||
fixed09 = 1000
|
||||
fixed10 = 1000
|
||||
fixed11 = 1000
|
||||
fixed12 = 1000
|
||||
fixed13 = 1000
|
||||
fixed14 = 1000
|
||||
fixed15 = 1000
|
||||
fixed16 = 1000
|
||||
fixed17 = 1000
|
||||
fixed18 = 1000
|
||||
fixed19 = 1000
|
||||
fixed20 = 1000
|
||||
total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
|
||||
varying01 = 1700
|
||||
varying02 = 1475
|
||||
end
|
||||
@@ -0,0 +1,166 @@
|
||||
{
|
||||
"recorded_utc": "2026-09-11T08:50:38.6429749Z",
|
||||
"compiler": [
|
||||
"rustc 1.98.1 (48a229cea 2026-09-01)",
|
||||
"binary: rustc",
|
||||
"commit-hash: 48a229ceaefd4985c50990b14116b6d856af0985",
|
||||
"commit-date: 2026-09-01",
|
||||
"host: x86_64-pc-windows-gnullvm",
|
||||
"release: 1.98.1",
|
||||
"LLVM version: 22.1.8"
|
||||
],
|
||||
"compile_command": "rustc --edition=2024 -D warnings -C linker=clang.exe harness/experiments/analysis-context-20260911/main.rs -o .local/analysis-context-20260911/analysis-context.exe",
|
||||
"run_command": ".local/analysis-context-20260911/analysis-context.exe harness/experiments/analysis-context-20260911/generated",
|
||||
"process_exit_code": 0,
|
||||
"assertions_passed": 218,
|
||||
"program_sha256": "5632ACAFD06413DCC9961DDC1ADE60C7556FB56B43ED3C667F5106E7559A3733",
|
||||
"executable_sha256": "36E2D5A7F0683CC19F2EB50CA4840F3A793A8350498E3E17EDD75710A78FC675",
|
||||
"results_sha256": "248419751E9B836CDE6D80F97202843DD5DC085D1BEC67CB2FCC5046960C143B",
|
||||
"fixture_hashes": [
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/B-factored-cleared-override.study",
|
||||
"sha256": "DD740295FB0F849A63815AE3174EB067616687A5D46BD63089A6AA279F9D48B8",
|
||||
"bytes": 2762
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/B-factored-cycle.rejected-study",
|
||||
"sha256": "4FF294ACA6EDBAD60CB6C899D2B1D93B8E07337A295DBC63876A4497DC9D58EA",
|
||||
"bytes": 2782
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/B-factored-fixed-all.study",
|
||||
"sha256": "A51BD1F3EF7153D67FB63EAB2B9906E9FFB0049A2179B47C632ABB3E9B01A0F4",
|
||||
"bytes": 2781
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/B-factored-frozen-case20.study",
|
||||
"sha256": "05370CCB21E0B0AB8F26F9704FBECA1BE44FA1156FBF6C8420E09C436D016476",
|
||||
"bytes": 2665
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/B-factored-missing.study",
|
||||
"sha256": "D6D1B53B307836275A417C3CB094881F91755D1FAD0D521CE9B90FE1FD8F039D",
|
||||
"bytes": 2784
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/B-factored-threshold.study",
|
||||
"sha256": "86C5DC5FCA452A9CD7FC13318D72BE81973802C13F878D488561551D27F62C67",
|
||||
"bytes": 2781
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/B-factored.study",
|
||||
"sha256": "0C2B9F055E4E6993A14688A0F58BD6629F724687DC694D7213166197B197B878",
|
||||
"bytes": 2781
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/B-inline-cleared-override.study",
|
||||
"sha256": "8FC77924459D892D1E91CF1756C708ED34005ED840F3A38B98474037BBEC4FA1",
|
||||
"bytes": 2262
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/B-inline-cycle.rejected-study",
|
||||
"sha256": "3D8249B3DDB21F36C765AF440F6DE0E9E7B125E6504A07BDD3BA83E7B94CE9E1",
|
||||
"bytes": 2282
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/B-inline-fixed-all.study",
|
||||
"sha256": "CD32AE6CDE2E21AFBB536109A9D2911ECC3572A9BE368C2D877EE3EB8F2D2303",
|
||||
"bytes": 2281
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/B-inline-frozen-case20.study",
|
||||
"sha256": "B09AF2449DD8E2DAEB5F0B4AE0FF9AC914227B6FE619B4B0139FEF0778B7FB7A",
|
||||
"bytes": 2165
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/B-inline-missing.study",
|
||||
"sha256": "0886DE0C602CCD06A7F7AC5C248C7EE9C5670C567880DCD3ED6D37E14C5AA524",
|
||||
"bytes": 2284
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/B-inline-threshold.study",
|
||||
"sha256": "54F518BCBB166264F763BC14AFA1437D4E95A3244DFE9DF0B0331591E72C85DD",
|
||||
"bytes": 2281
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/B-inline.study",
|
||||
"sha256": "87A97378550BB79DA9A0CAD453700352D9E24C289E6865BA9D8CF554C62FEE36",
|
||||
"bytes": 2281
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/D-factored-cycle.rejected-study",
|
||||
"sha256": "DAC04A19992313549D322D70DF564EF9026FBD5F30402DDA591AF579B7F70E26",
|
||||
"bytes": 14027
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/D-factored-deleted-row.rejected-study",
|
||||
"sha256": "C00EF4080857E18ADCC309FE57D512CA681E28A46E259B44A34D3852A334E973",
|
||||
"bytes": 14007
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/D-factored-fixed-all.study",
|
||||
"sha256": "D746830E424702ACE35B5A26EFE8D2E96D4D2CC028DC2B7E207E6326D0F22597",
|
||||
"bytes": 14026
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/D-factored-frozen-case20.study",
|
||||
"sha256": "804F0D3DB792659BFAA1CFB0CF96BF243C379F6971EC1FE357FE98CDF3269ED6",
|
||||
"bytes": 2167
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/D-factored-missing.study",
|
||||
"sha256": "32351A6F8BA499AAAD644FABFB08F7D6A067365394B2B6AD33EE7581A9761889",
|
||||
"bytes": 14029
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/D-factored-threshold.study",
|
||||
"sha256": "20EC6C0EB3EB599BE6354F2BED4BB0EF07747B4EE0B9CB80B757C59633A4579C",
|
||||
"bytes": 14026
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/D-factored.study",
|
||||
"sha256": "17C9CEB39E9B39D867EF732D4DE8BA36C00546BE1BC987547A52B99EEA83BE99",
|
||||
"bytes": 14026
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/D-inline-cycle.rejected-study",
|
||||
"sha256": "2C7443440051B7DD31733B7814C86FFC53BE5F7EBDFFE56925B9F9E60693FD84",
|
||||
"bytes": 13242
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/D-inline-deleted-row.rejected-study",
|
||||
"sha256": "26970B159D67A5E858298EC77C6B7FFA233EC3EF0FB903E9E860795DE829F28C",
|
||||
"bytes": 13222
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/D-inline-fixed-all.study",
|
||||
"sha256": "39AEC6A7457B4A8D9D9518B4B29A8185048811329B7614794FF69E122A972D6C",
|
||||
"bytes": 13241
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/D-inline-fixed-one-only.study",
|
||||
"sha256": "83E9F73E7E8CE4AC300979CE1D120707C4B7DD4A1D076C8D9BAB4BC8CF8230E8",
|
||||
"bytes": 13241
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/D-inline-frozen-case20.study",
|
||||
"sha256": "88A1A971651E827399775A7D9F6AAD32B7CE1C3E0F78D57E7AC72F207495E2B6",
|
||||
"bytes": 1667
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/D-inline-missing.study",
|
||||
"sha256": "8F44E9524A1BFCC96102C2C91CE15C4A87E698509225FD84D9A860EF8F7A23E6",
|
||||
"bytes": 13244
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/D-inline-threshold.study",
|
||||
"sha256": "59C77A7DF1488E96B994FE04A32A505B8DFC5F3F92C45A8BD774A043C1C2F5A0",
|
||||
"bytes": 13241
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/analysis-context-20260911/generated/D-inline.study",
|
||||
"sha256": "7A0FF1A4CCAD6B41344331182820AB6C6C7990DA09B842F8D04C60008A8D5232",
|
||||
"bytes": 13241
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# Executed context experiment
|
||||
|
||||
All input magnitudes are integer milliseconds. This tiny dialect is not Studio source.
|
||||
|
||||
| Variant | Initial lines | Initial bytes | Binding rows | Fixed-all edit removed/added lines | Threshold edit removed/added lines | Initial passing cases | After fixed-all change |
|
||||
| --- | ---: | ---: | ---: | --- | --- | ---: | ---: |
|
||||
| B-inline | 106 | 2281 | 62 | 3/3 | 3/3 | 4 | 1 |
|
||||
| B-factored | 108 | 2781 | 62 | 3/3 | 2/2 | 4 | 1 |
|
||||
| D-inline | 522 | 13241 | 480 | 21/21 | 21/21 | 4 | 1 |
|
||||
| D-factored | 524 | 14026 | 480 | 21/21 | 2/2 | 4 | 1 |
|
||||
|
||||
Executed assertions: 218. All passed. Every accepted fixture was parsed, validated, rendered and parsed back with exact experiment AST equality. Rejected cases were checked separately.
|
||||
|
||||
Negative controls: D changing one repeated fixed input changes only that case; B evaluating inherited formulas against baseline inputs instead of effective case inputs gives 21,750 instead of case20's 23,175. A shallow B case copy is not frozen; factored D also needs its definition environment snapshotted. Clearing a B override restores its named baseline value, including an explicitly missing baseline. A deleted D binding row rejects. Baseline chains, recursive definitions and project captures inside pure functions reject.
|
||||
@@ -0,0 +1,913 @@
|
||||
//! Isolated context experiment. This is deliberately NOT Studio syntax or production code.
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fmt::Write as _;
|
||||
use std::path::Path;
|
||||
|
||||
type Bindings = BTreeMap<String, Expr>;
|
||||
type Functions = BTreeMap<String, Function>;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
enum Expr {
|
||||
Int(i64),
|
||||
Missing,
|
||||
Ref(String),
|
||||
Sum(Vec<Expr>),
|
||||
Le(Box<Expr>, Box<Expr>),
|
||||
Call(String, Vec<Expr>),
|
||||
}
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct Function {
|
||||
parameters: Vec<String>,
|
||||
body: Expr,
|
||||
}
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct Baseline {
|
||||
name: String,
|
||||
revision: u64,
|
||||
bindings: Bindings,
|
||||
}
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct Case {
|
||||
name: String,
|
||||
base: Option<String>,
|
||||
bindings: Bindings,
|
||||
}
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct Study {
|
||||
mode: char,
|
||||
revision: u64,
|
||||
properties: Vec<String>,
|
||||
functions: Functions,
|
||||
baseline: Option<Baseline>,
|
||||
cases: Vec<Case>,
|
||||
origin: Option<String>,
|
||||
captured_origins: BTreeMap<String, String>,
|
||||
}
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
enum Value {
|
||||
Int(i64),
|
||||
Bool(bool),
|
||||
Missing(BTreeSet<String>),
|
||||
}
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct Observation {
|
||||
value: Value,
|
||||
origins: BTreeMap<String, String>,
|
||||
document_revision: u64,
|
||||
}
|
||||
|
||||
fn split_args(s: &str) -> Result<Vec<&str>, String> {
|
||||
if s.trim().is_empty() {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
let mut depth = 0_i32;
|
||||
let mut start = 0;
|
||||
let mut out = vec![];
|
||||
for (i, ch) in s.char_indices() {
|
||||
match ch {
|
||||
'(' => depth += 1,
|
||||
')' => {
|
||||
depth -= 1;
|
||||
if depth < 0 {
|
||||
return Err("unbalanced expression".into());
|
||||
}
|
||||
}
|
||||
',' if depth == 0 => {
|
||||
out.push(s[start..i].trim());
|
||||
start = i + 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if depth != 0 {
|
||||
return Err("unbalanced expression".into());
|
||||
}
|
||||
out.push(s[start..].trim());
|
||||
Ok(out)
|
||||
}
|
||||
fn expr(s: &str) -> Result<Expr, String> {
|
||||
let s = s.trim();
|
||||
if s == "missing" {
|
||||
return Ok(Expr::Missing);
|
||||
}
|
||||
if let Ok(n) = s.parse() {
|
||||
return Ok(Expr::Int(n));
|
||||
}
|
||||
if let Some(at) = s.find('(') {
|
||||
if !s.ends_with(')') {
|
||||
return Err("missing closing parenthesis".into());
|
||||
}
|
||||
let args = split_args(&s[at + 1..s.len() - 1])?
|
||||
.into_iter()
|
||||
.map(expr)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
return match &s[..at] {
|
||||
"sum" => Ok(Expr::Sum(args)),
|
||||
"le" if args.len() == 2 => Ok(Expr::Le(
|
||||
Box::new(args[0].clone()),
|
||||
Box::new(args[1].clone()),
|
||||
)),
|
||||
"le" => Err("le requires two arguments".into()),
|
||||
name => Ok(Expr::Call(name.into(), args)),
|
||||
};
|
||||
}
|
||||
if s.is_empty() || !s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
|
||||
return Err(format!("invalid symbol {s}"));
|
||||
}
|
||||
Ok(Expr::Ref(s.into()))
|
||||
}
|
||||
fn spelling(e: &Expr) -> String {
|
||||
let list = |xs: &[Expr]| xs.iter().map(spelling).collect::<Vec<_>>().join(", ");
|
||||
match e {
|
||||
Expr::Int(v) => v.to_string(),
|
||||
Expr::Missing => "missing".into(),
|
||||
Expr::Ref(s) => s.clone(),
|
||||
Expr::Sum(xs) => format!("sum({})", list(xs)),
|
||||
Expr::Le(a, b) => format!("le({}, {})", spelling(a), spelling(b)),
|
||||
Expr::Call(name, xs) => format!("{name}({})", list(xs)),
|
||||
}
|
||||
}
|
||||
fn parse(text: &str) -> Result<Study, String> {
|
||||
let mut lines = text.lines().filter(|l| !l.trim().is_empty());
|
||||
let head = lines
|
||||
.next()
|
||||
.ok_or("empty study")?
|
||||
.split_whitespace()
|
||||
.collect::<Vec<_>>();
|
||||
if head.len() != 4
|
||||
|| head[0] != "study"
|
||||
|| head[2] != "revision"
|
||||
|| !matches!(head[1], "B" | "D")
|
||||
{
|
||||
return Err("invalid study header".into());
|
||||
}
|
||||
let mut study = Study {
|
||||
mode: head[1].chars().next().unwrap(),
|
||||
revision: head[3].parse().map_err(|_| "bad revision")?,
|
||||
properties: vec![],
|
||||
functions: BTreeMap::new(),
|
||||
baseline: None,
|
||||
cases: vec![],
|
||||
origin: None,
|
||||
captured_origins: BTreeMap::new(),
|
||||
};
|
||||
let mut current: Option<(bool, Case, u64)> = None;
|
||||
for line in lines {
|
||||
let line = line.trim();
|
||||
if line == "end" {
|
||||
let (base, item, revision) = current.take().ok_or("unmatched end")?;
|
||||
if base {
|
||||
if study.baseline.is_some() {
|
||||
return Err("multiple baselines forbidden".into());
|
||||
}
|
||||
study.baseline = Some(Baseline {
|
||||
name: item.name,
|
||||
revision,
|
||||
bindings: item.bindings,
|
||||
});
|
||||
} else {
|
||||
if study.cases.iter().any(|c| c.name == item.name) {
|
||||
return Err("duplicate case".into());
|
||||
}
|
||||
study.cases.push(item);
|
||||
}
|
||||
} else if let Some((_, case, _)) = current.as_mut() {
|
||||
let (name, value) = line.split_once(" = ").ok_or("expected binding")?;
|
||||
if case.bindings.insert(name.into(), expr(value)?).is_some() {
|
||||
return Err("duplicate binding".into());
|
||||
}
|
||||
} else if let Some(properties) = line.strip_prefix("properties ") {
|
||||
if !study.properties.is_empty() {
|
||||
return Err("duplicate properties".into());
|
||||
}
|
||||
study.properties = properties.split(", ").map(String::from).collect();
|
||||
} else if let Some(origin) = line.strip_prefix("origin ") {
|
||||
study.origin = Some(origin.into());
|
||||
} else if let Some(captured) = line.strip_prefix("captured ") {
|
||||
let (property, origin) = captured
|
||||
.split_once(" = ")
|
||||
.ok_or("invalid captured origin")?;
|
||||
if study
|
||||
.captured_origins
|
||||
.insert(property.into(), origin.into())
|
||||
.is_some()
|
||||
{
|
||||
return Err("duplicate captured origin".into());
|
||||
}
|
||||
} else if let Some(def) = line.strip_prefix("function ") {
|
||||
let (left, right) = def.split_once(" = ").ok_or("invalid function")?;
|
||||
let at = left.find('(').ok_or("function parameters required")?;
|
||||
if !left.ends_with(')') {
|
||||
return Err("bad parameters".into());
|
||||
}
|
||||
let f = Function {
|
||||
parameters: split_args(&left[at + 1..left.len() - 1])?
|
||||
.into_iter()
|
||||
.map(String::from)
|
||||
.collect(),
|
||||
body: expr(right)?,
|
||||
};
|
||||
if study.functions.insert(left[..at].into(), f).is_some() {
|
||||
return Err("duplicate function".into());
|
||||
}
|
||||
} else {
|
||||
let words = line.split_whitespace().collect::<Vec<_>>();
|
||||
let (base, name, parent, revision) = match words.as_slice() {
|
||||
["baseline", name, "revision", revision] => (
|
||||
true,
|
||||
*name,
|
||||
None,
|
||||
revision.parse().map_err(|_| "bad baseline revision")?,
|
||||
),
|
||||
["case", name, "base", parent] => (false, *name, Some((*parent).to_owned()), 0),
|
||||
["case", name, "independent"] => (false, *name, None, 0),
|
||||
_ => return Err(format!("invalid declaration: {line}")),
|
||||
};
|
||||
current = Some((
|
||||
base,
|
||||
Case {
|
||||
name: name.into(),
|
||||
base: parent,
|
||||
bindings: BTreeMap::new(),
|
||||
},
|
||||
revision,
|
||||
));
|
||||
}
|
||||
}
|
||||
if current.is_some() {
|
||||
return Err("unterminated block".into());
|
||||
}
|
||||
validate(&study)?;
|
||||
Ok(study)
|
||||
}
|
||||
fn render(s: &Study) -> String {
|
||||
let mut text = format!("study {} revision {}\n", s.mode, s.revision);
|
||||
if let Some(origin) = &s.origin {
|
||||
writeln!(text, "origin {origin}").unwrap();
|
||||
}
|
||||
for (property, origin) in &s.captured_origins {
|
||||
writeln!(text, "captured {property} = {origin}").unwrap();
|
||||
}
|
||||
writeln!(text, "properties {}", s.properties.join(", ")).unwrap();
|
||||
for (name, f) in &s.functions {
|
||||
writeln!(
|
||||
text,
|
||||
"function {name}({}) = {}",
|
||||
f.parameters.join(", "),
|
||||
spelling(&f.body)
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
let write_bindings = |text: &mut String, bindings: &Bindings| {
|
||||
for (p, e) in bindings {
|
||||
writeln!(text, " {p} = {}", spelling(e)).unwrap();
|
||||
}
|
||||
text.push_str("end\n");
|
||||
};
|
||||
if let Some(b) = &s.baseline {
|
||||
writeln!(text, "baseline {} revision {}", b.name, b.revision).unwrap();
|
||||
write_bindings(&mut text, &b.bindings);
|
||||
}
|
||||
for c in &s.cases {
|
||||
writeln!(
|
||||
text,
|
||||
"case {} {}",
|
||||
c.name,
|
||||
c.base
|
||||
.as_ref()
|
||||
.map_or("independent".into(), |b| format!("base {b}"))
|
||||
)
|
||||
.unwrap();
|
||||
write_bindings(&mut text, &c.bindings);
|
||||
}
|
||||
text
|
||||
}
|
||||
fn refs(e: &Expr, names: &mut BTreeSet<String>, calls: &mut BTreeSet<String>) {
|
||||
match e {
|
||||
Expr::Ref(p) => {
|
||||
names.insert(p.clone());
|
||||
}
|
||||
Expr::Sum(xs) | Expr::Call(_, xs) => {
|
||||
if let Expr::Call(f, _) = e {
|
||||
calls.insert(f.clone());
|
||||
}
|
||||
for x in xs {
|
||||
refs(x, names, calls);
|
||||
}
|
||||
}
|
||||
Expr::Le(a, b) => {
|
||||
refs(a, names, calls);
|
||||
refs(b, names, calls);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
fn acyclic(graph: &BTreeMap<String, BTreeSet<String>>) -> Result<(), String> {
|
||||
fn visit(
|
||||
n: &str,
|
||||
graph: &BTreeMap<String, BTreeSet<String>>,
|
||||
path: &mut Vec<String>,
|
||||
done: &mut BTreeSet<String>,
|
||||
) -> Result<(), String> {
|
||||
if path.iter().any(|p| p == n) {
|
||||
path.push(n.into());
|
||||
return Err(format!("cycle: {}", path.join(" -> ")));
|
||||
}
|
||||
if done.contains(n) {
|
||||
return Ok(());
|
||||
}
|
||||
path.push(n.into());
|
||||
for next in graph
|
||||
.get(n)
|
||||
.ok_or_else(|| format!("unknown dependency {n}"))?
|
||||
{
|
||||
visit(next, graph, path, done)?;
|
||||
}
|
||||
path.pop();
|
||||
done.insert(n.into());
|
||||
Ok(())
|
||||
}
|
||||
let mut done = BTreeSet::new();
|
||||
for n in graph.keys() {
|
||||
visit(n, graph, &mut vec![], &mut done)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn resolved<'a>(s: &'a Study, c: &'a Case, property: &str) -> Result<(&'a Expr, String), String> {
|
||||
if let Some(e) = c.bindings.get(property) {
|
||||
return Ok((e, format!("case:{}", c.name)));
|
||||
}
|
||||
if let Some(base) = &c.base {
|
||||
let b = s
|
||||
.baseline
|
||||
.as_ref()
|
||||
.filter(|b| &b.name == base)
|
||||
.ok_or("unknown baseline")?;
|
||||
if let Some(e) = b.bindings.get(property) {
|
||||
return Ok((
|
||||
e,
|
||||
format!("baseline:{}@{} via {}", b.name, b.revision, c.name),
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(format!("missing binding row: {}.{property}", c.name))
|
||||
}
|
||||
fn validate(s: &Study) -> Result<(), String> {
|
||||
let properties = s.properties.iter().cloned().collect::<BTreeSet<_>>();
|
||||
if properties.len() != s.properties.len() || properties.is_empty() {
|
||||
return Err("invalid property catalog".into());
|
||||
}
|
||||
if (s.mode == 'B') != s.baseline.is_some() {
|
||||
return Err("B requires one explicit baseline; D forbids baselines".into());
|
||||
}
|
||||
let mut function_graph = BTreeMap::new();
|
||||
for (name, f) in &s.functions {
|
||||
let mut names = BTreeSet::new();
|
||||
let mut calls = BTreeSet::new();
|
||||
refs(&f.body, &mut names, &mut calls);
|
||||
let parameters = f.parameters.iter().cloned().collect::<BTreeSet<_>>();
|
||||
if parameters.len() != f.parameters.len() || !names.is_subset(¶meters) {
|
||||
return Err(format!("function {name} captures unknown/project property"));
|
||||
}
|
||||
function_graph.insert(name.clone(), calls);
|
||||
}
|
||||
acyclic(&function_graph)?;
|
||||
let mut contexts = s.cases.clone();
|
||||
if let Some(b) = &s.baseline {
|
||||
contexts.push(Case {
|
||||
name: format!("baseline_{}", b.name),
|
||||
base: None,
|
||||
bindings: b.bindings.clone(),
|
||||
});
|
||||
}
|
||||
for c in &contexts {
|
||||
if !c.bindings.keys().all(|p| properties.contains(p)) {
|
||||
return Err("unknown binding property".into());
|
||||
}
|
||||
if s.mode == 'D' && c.base.is_some() {
|
||||
return Err("D case cannot inherit".into());
|
||||
}
|
||||
if let Some(name) = &c.base {
|
||||
if s.baseline.as_ref().is_none_or(|b| &b.name != name) {
|
||||
return Err("unknown baseline; case-to-case inheritance forbidden".into());
|
||||
}
|
||||
}
|
||||
let mut graph = BTreeMap::new();
|
||||
for p in &properties {
|
||||
let (e, _) = resolved(s, c, p)?;
|
||||
let mut names = BTreeSet::new();
|
||||
let mut calls = BTreeSet::new();
|
||||
refs(e, &mut names, &mut calls);
|
||||
if !names.is_subset(&properties) || !calls.iter().all(|f| s.functions.contains_key(f)) {
|
||||
return Err("unknown reference".into());
|
||||
}
|
||||
graph.insert(p.clone(), names);
|
||||
}
|
||||
acyclic(&graph).map_err(|e| format!("{}: {e}", c.name))?;
|
||||
for p in &properties {
|
||||
evaluate(s, c, p)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn evaluate(s: &Study, c: &Case, property: &str) -> Result<Observation, String> {
|
||||
let mut origins = BTreeMap::new();
|
||||
let mut cache = BTreeMap::new();
|
||||
fn property_value(
|
||||
s: &Study,
|
||||
c: &Case,
|
||||
p: &str,
|
||||
origins: &mut BTreeMap<String, String>,
|
||||
cache: &mut BTreeMap<String, Value>,
|
||||
) -> Result<Value, String> {
|
||||
if let Some(v) = cache.get(p) {
|
||||
return Ok(v.clone());
|
||||
}
|
||||
let (e, origin) = resolved(s, c, p)?;
|
||||
origins.insert(
|
||||
p.into(),
|
||||
s.captured_origins
|
||||
.get(p)
|
||||
.map_or(origin, |old| format!("frozen:{old}")),
|
||||
);
|
||||
let value = run(s, c, e, &BTreeMap::new(), Some(p), origins, cache)?;
|
||||
if !matches!(value, Value::Missing(_)) && (p == "budget") != matches!(value, Value::Bool(_))
|
||||
{
|
||||
return Err(format!("wrong type for {p}"));
|
||||
}
|
||||
cache.insert(p.into(), value.clone());
|
||||
Ok(value)
|
||||
}
|
||||
fn run(
|
||||
s: &Study,
|
||||
c: &Case,
|
||||
e: &Expr,
|
||||
params: &BTreeMap<String, Value>,
|
||||
missing_property: Option<&str>,
|
||||
origins: &mut BTreeMap<String, String>,
|
||||
cache: &mut BTreeMap<String, Value>,
|
||||
) -> Result<Value, String> {
|
||||
match e {
|
||||
Expr::Int(n) => Ok(Value::Int(*n)),
|
||||
Expr::Missing => Ok(Value::Missing(BTreeSet::from([missing_property
|
||||
.unwrap_or("function-body")
|
||||
.into()]))),
|
||||
Expr::Ref(p) => params
|
||||
.get(p)
|
||||
.cloned()
|
||||
.map_or_else(|| property_value(s, c, p, origins, cache), Ok),
|
||||
Expr::Call(name, args) => {
|
||||
let f = s.functions.get(name).ok_or("unknown function")?;
|
||||
if f.parameters.len() != args.len() {
|
||||
return Err("wrong function arity".into());
|
||||
}
|
||||
let mut bound = BTreeMap::new();
|
||||
for (parameter, arg) in f.parameters.iter().zip(args) {
|
||||
bound.insert(
|
||||
parameter.clone(),
|
||||
run(s, c, arg, params, missing_property, origins, cache)?,
|
||||
);
|
||||
}
|
||||
run(s, c, &f.body, &bound, missing_property, origins, cache)
|
||||
}
|
||||
Expr::Sum(xs) => {
|
||||
let mut total = 0_i64;
|
||||
let mut missing = BTreeSet::new();
|
||||
for x in xs {
|
||||
match run(s, c, x, params, missing_property, origins, cache)? {
|
||||
Value::Int(n) => total = total.checked_add(n).ok_or("integer overflow")?,
|
||||
Value::Missing(m) => missing.extend(m),
|
||||
Value::Bool(_) => return Err("sum requires numeric inputs".into()),
|
||||
}
|
||||
}
|
||||
Ok(if missing.is_empty() {
|
||||
Value::Int(total)
|
||||
} else {
|
||||
Value::Missing(missing)
|
||||
})
|
||||
}
|
||||
Expr::Le(a, b) => {
|
||||
let a = run(s, c, a, params, missing_property, origins, cache)?;
|
||||
let b = run(s, c, b, params, missing_property, origins, cache)?;
|
||||
match (a, b) {
|
||||
(Value::Int(a), Value::Int(b)) => Ok(Value::Bool(a <= b)),
|
||||
(Value::Missing(mut a), Value::Missing(b)) => {
|
||||
a.extend(b);
|
||||
Ok(Value::Missing(a))
|
||||
}
|
||||
(Value::Missing(a), Value::Int(_)) | (Value::Int(_), Value::Missing(a)) => {
|
||||
Ok(Value::Missing(a))
|
||||
}
|
||||
_ => Err("le requires numeric inputs".into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let value = property_value(s, c, property, &mut origins, &mut cache)?;
|
||||
Ok(Observation {
|
||||
value,
|
||||
origins,
|
||||
document_revision: s.revision,
|
||||
})
|
||||
}
|
||||
fn fixture(mode: char, factored: bool) -> Study {
|
||||
let inputs = (1..=20)
|
||||
.map(|i| format!("fixed{i:02}"))
|
||||
.chain(["varying01".into(), "varying02".into()])
|
||||
.collect::<Vec<_>>();
|
||||
let mut all = inputs.clone();
|
||||
all.extend(["total".into(), "budget".into()]);
|
||||
let mut bindings = (1..=20)
|
||||
.map(|i| (format!("fixed{i:02}"), Expr::Int(1000)))
|
||||
.collect::<Bindings>();
|
||||
bindings.insert("varying01".into(), Expr::Int(750));
|
||||
bindings.insert("varying02".into(), Expr::Int(1000));
|
||||
let total = Expr::Sum(inputs.iter().cloned().map(Expr::Ref).collect());
|
||||
let check = Expr::Le(
|
||||
Box::new(Expr::Ref("total".into())),
|
||||
Box::new(Expr::Int(22000)),
|
||||
);
|
||||
let mut functions = BTreeMap::new();
|
||||
if factored {
|
||||
functions.insert(
|
||||
"total_formula".into(),
|
||||
Function {
|
||||
parameters: inputs.clone(),
|
||||
body: total,
|
||||
},
|
||||
);
|
||||
functions.insert(
|
||||
"budget_formula".into(),
|
||||
Function {
|
||||
parameters: vec!["result".into()],
|
||||
body: Expr::Le(
|
||||
Box::new(Expr::Ref("result".into())),
|
||||
Box::new(Expr::Int(22000)),
|
||||
),
|
||||
},
|
||||
);
|
||||
bindings.insert(
|
||||
"total".into(),
|
||||
Expr::Call(
|
||||
"total_formula".into(),
|
||||
inputs.into_iter().map(Expr::Ref).collect(),
|
||||
),
|
||||
);
|
||||
bindings.insert(
|
||||
"budget".into(),
|
||||
Expr::Call("budget_formula".into(), vec![Expr::Ref("total".into())]),
|
||||
);
|
||||
} else {
|
||||
bindings.insert("total".into(), total);
|
||||
bindings.insert("budget".into(), check);
|
||||
}
|
||||
let cases = (0..20)
|
||||
.map(|i| {
|
||||
let mut rows = if mode == 'D' {
|
||||
bindings.clone()
|
||||
} else {
|
||||
BTreeMap::new()
|
||||
};
|
||||
if mode == 'D' || i != 0 {
|
||||
rows.insert("varying01".into(), Expr::Int(750 + 50 * i));
|
||||
rows.insert("varying02".into(), Expr::Int(1000 + 25 * i));
|
||||
}
|
||||
Case {
|
||||
name: format!("case{:02}", i + 1),
|
||||
base: (mode == 'B').then(|| "engineering".into()),
|
||||
bindings: rows,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Study {
|
||||
mode,
|
||||
revision: 1,
|
||||
properties: all,
|
||||
functions,
|
||||
baseline: (mode == 'B').then(|| Baseline {
|
||||
name: "engineering".into(),
|
||||
revision: 1,
|
||||
bindings,
|
||||
}),
|
||||
cases,
|
||||
origin: None,
|
||||
captured_origins: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
fn freeze(s: &Study, case: usize) -> Study {
|
||||
let c = &s.cases[case];
|
||||
Study {
|
||||
mode: 'D',
|
||||
revision: 1,
|
||||
properties: s.properties.clone(),
|
||||
functions: s.functions.clone(),
|
||||
baseline: None,
|
||||
cases: vec![Case {
|
||||
name: c.name.clone(),
|
||||
base: None,
|
||||
bindings: s
|
||||
.properties
|
||||
.iter()
|
||||
.map(|p| (p.clone(), resolved(s, c, p).unwrap().0.clone()))
|
||||
.collect(),
|
||||
}],
|
||||
origin: Some(format!(
|
||||
"{}:{}:document{}{}",
|
||||
s.mode,
|
||||
c.name,
|
||||
s.revision,
|
||||
s.baseline
|
||||
.as_ref()
|
||||
.map_or(String::new(), |b| format!(":{}@{}", b.name, b.revision))
|
||||
)),
|
||||
captured_origins: s
|
||||
.properties
|
||||
.iter()
|
||||
.map(|p| (p.clone(), resolved(s, c, p).unwrap().1))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
fn write_fixture(dir: &Path, name: &str, s: &Study) -> Result<String, String> {
|
||||
let source = render(s);
|
||||
let back = parse(&source)?;
|
||||
assert_eq!(&back, s, "exact experiment AST round trip");
|
||||
std::fs::write(dir.join(format!("{name}.study")), &source).map_err(|e| e.to_string())?;
|
||||
Ok(source)
|
||||
}
|
||||
fn diff(before: &str, after: &str) -> (usize, usize) {
|
||||
let a = before.lines().collect::<Vec<_>>();
|
||||
let b = after.lines().collect::<Vec<_>>();
|
||||
let mut row = vec![0; b.len() + 1];
|
||||
for x in &a {
|
||||
let mut diagonal = 0;
|
||||
for (j, y) in b.iter().enumerate() {
|
||||
let old = row[j + 1];
|
||||
row[j + 1] = if x == y {
|
||||
diagonal + 1
|
||||
} else {
|
||||
row[j + 1].max(row[j])
|
||||
};
|
||||
diagonal = old;
|
||||
}
|
||||
}
|
||||
(a.len() - row[b.len()], b.len() - row[b.len()])
|
||||
}
|
||||
fn assert_total(s: &Study, index: usize, expected: i64) {
|
||||
assert_eq!(
|
||||
evaluate(s, &s.cases[index], "total").unwrap().value,
|
||||
Value::Int(expected)
|
||||
);
|
||||
}
|
||||
fn change_fixed(s: &mut Study, all: bool) {
|
||||
s.revision += 1;
|
||||
if let Some(b) = s.baseline.as_mut() {
|
||||
b.revision += 1;
|
||||
b.bindings.insert("fixed01".into(), Expr::Int(1200));
|
||||
} else {
|
||||
for c in s.cases.iter_mut().take(if all { 20 } else { 1 }) {
|
||||
c.bindings.insert("fixed01".into(), Expr::Int(1200));
|
||||
}
|
||||
}
|
||||
}
|
||||
fn change_threshold(s: &mut Study, value: i64) {
|
||||
s.revision += 1;
|
||||
if let Some(f) = s.functions.get_mut("budget_formula") {
|
||||
f.body = Expr::Le(
|
||||
Box::new(Expr::Ref("result".into())),
|
||||
Box::new(Expr::Int(value)),
|
||||
);
|
||||
} else {
|
||||
let e = Expr::Le(
|
||||
Box::new(Expr::Ref("total".into())),
|
||||
Box::new(Expr::Int(value)),
|
||||
);
|
||||
if let Some(b) = s.baseline.as_mut() {
|
||||
b.revision += 1;
|
||||
b.bindings.insert("budget".into(), e);
|
||||
} else {
|
||||
for c in &mut s.cases {
|
||||
c.bindings.insert("budget".into(), e.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn pass_count(s: &Study) -> usize {
|
||||
s.cases
|
||||
.iter()
|
||||
.filter(|c| evaluate(s, c, "budget").unwrap().value == Value::Bool(true))
|
||||
.count()
|
||||
}
|
||||
|
||||
fn main() -> Result<(), String> {
|
||||
let out = std::env::args()
|
||||
.nth(1)
|
||||
.ok_or("usage: analysis-context.exe OUTPUT_DIRECTORY")?;
|
||||
let dir = Path::new(&out);
|
||||
std::fs::create_dir_all(dir).map_err(|e| e.to_string())?;
|
||||
let mut report = String::from(
|
||||
"# Executed context experiment\n\nAll input magnitudes are integer milliseconds. This tiny dialect is not Studio source.\n\n| Variant | Initial lines | Initial bytes | Binding rows | Fixed-all edit removed/added lines | Threshold edit removed/added lines | Initial passing cases | After fixed-all change |\n| --- | ---: | ---: | ---: | --- | --- | ---: | ---: |\n",
|
||||
);
|
||||
let mut checks = 0;
|
||||
for mode in ['B', 'D'] {
|
||||
for factored in [false, true] {
|
||||
let name = format!("{mode}-{}", if factored { "factored" } else { "inline" });
|
||||
let original = fixture(mode, factored);
|
||||
validate(&original)?;
|
||||
for i in 0..20 {
|
||||
assert_total(&original, i, 21750 + 75 * i as i64);
|
||||
checks += 1;
|
||||
}
|
||||
assert_eq!(pass_count(&original), 4);
|
||||
checks += 1;
|
||||
let initial = write_fixture(dir, &name, &original)?;
|
||||
let mut changed = original.clone();
|
||||
change_fixed(&mut changed, true);
|
||||
validate(&changed)?;
|
||||
for i in 0..20 {
|
||||
assert_total(&changed, i, 21950 + 75 * i as i64);
|
||||
checks += 1;
|
||||
}
|
||||
assert_eq!(pass_count(&changed), 1);
|
||||
checks += 1;
|
||||
let fixed_source = write_fixture(dir, &format!("{name}-fixed-all"), &changed)?;
|
||||
let mut threshold = original.clone();
|
||||
change_threshold(&mut threshold, 22500);
|
||||
validate(&threshold)?;
|
||||
assert_eq!(pass_count(&threshold), 11);
|
||||
checks += 1;
|
||||
let threshold_source = write_fixture(dir, &format!("{name}-threshold"), &threshold)?;
|
||||
let frozen = freeze(&original, 19);
|
||||
let frozen_before = evaluate(&frozen, &frozen.cases[0], "budget")?;
|
||||
assert_total(&frozen, 0, 23175);
|
||||
checks += 1;
|
||||
change_threshold(&mut changed, 25000);
|
||||
assert_eq!(
|
||||
evaluate(&frozen, &frozen.cases[0], "budget")?,
|
||||
frozen_before
|
||||
);
|
||||
checks += 1;
|
||||
write_fixture(dir, &format!("{name}-frozen-case20"), &frozen)?;
|
||||
// A shallow case copy still follows its baseline and/or shared function environment.
|
||||
let shallow = original.cases[19].clone();
|
||||
if mode == 'B' {
|
||||
assert_eq!(
|
||||
evaluate(&changed, &shallow, "total")?.value,
|
||||
Value::Int(23375)
|
||||
);
|
||||
checks += 1;
|
||||
}
|
||||
if factored {
|
||||
assert_eq!(
|
||||
evaluate(&changed, &shallow, "budget")?.value,
|
||||
Value::Bool(true)
|
||||
);
|
||||
checks += 1;
|
||||
}
|
||||
if mode == 'D' && !factored {
|
||||
assert_eq!(
|
||||
evaluate(&changed, &shallow, "total")?.value,
|
||||
Value::Int(23175)
|
||||
);
|
||||
assert_eq!(
|
||||
evaluate(&changed, &shallow, "budget")?.value,
|
||||
Value::Bool(false)
|
||||
);
|
||||
checks += 2;
|
||||
}
|
||||
assert_eq!(frozen.captured_origins.len(), 24);
|
||||
assert!(
|
||||
evaluate(&frozen, &frozen.cases[0], "total")?
|
||||
.origins
|
||||
.values()
|
||||
.all(|origin| origin.starts_with("frozen:"))
|
||||
);
|
||||
checks += 2;
|
||||
let mut missing = original.clone();
|
||||
missing.cases[19]
|
||||
.bindings
|
||||
.insert("varying01".into(), Expr::Missing);
|
||||
validate(&missing)?;
|
||||
assert_eq!(
|
||||
evaluate(&missing, &missing.cases[19], "budget")?.value,
|
||||
Value::Missing(BTreeSet::from(["varying01".into()]))
|
||||
);
|
||||
checks += 1;
|
||||
write_fixture(dir, &format!("{name}-missing"), &missing)?;
|
||||
let frozen_missing = freeze(&missing, 19);
|
||||
assert_eq!(
|
||||
evaluate(&frozen_missing, &frozen_missing.cases[0], "budget")?.value,
|
||||
Value::Missing(BTreeSet::from(["varying01".into()]))
|
||||
);
|
||||
checks += 1;
|
||||
missing.cases[19].bindings.remove("varying01");
|
||||
if mode == 'B' {
|
||||
validate(&missing)?;
|
||||
assert_total(&missing, 19, 22225);
|
||||
checks += 1;
|
||||
write_fixture(dir, &format!("{name}-cleared-override"), &missing)?;
|
||||
} else {
|
||||
assert!(
|
||||
validate(&missing)
|
||||
.unwrap_err()
|
||||
.contains("missing binding row")
|
||||
);
|
||||
checks += 1;
|
||||
std::fs::write(
|
||||
dir.join(format!("{name}-deleted-row.rejected-study")),
|
||||
render(&missing),
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
let mut cyclic = original.clone();
|
||||
cyclic.cases[19]
|
||||
.bindings
|
||||
.insert("varying01".into(), Expr::Ref("total".into()));
|
||||
let cycle = validate(&cyclic).unwrap_err();
|
||||
assert!(cycle.contains("cycle"));
|
||||
checks += 1;
|
||||
std::fs::write(
|
||||
dir.join(format!("{name}-cycle.rejected-study")),
|
||||
render(&cyclic),
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let observation = evaluate(&original, &original.cases[19], "budget")?;
|
||||
let mut detail = format!(
|
||||
"# {name}: case20 evaluation\n\nValue: {:?}\nDocument revision: {}\n\n",
|
||||
observation.value, observation.document_revision
|
||||
);
|
||||
for (p, origin) in &observation.origins {
|
||||
writeln!(detail, "- {p}: {origin}").unwrap();
|
||||
}
|
||||
writeln!(detail, "\nRejected cycle: {cycle}").unwrap();
|
||||
std::fs::write(dir.join(format!("{name}-provenance.md")), detail)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rows = original.baseline.as_ref().map_or(0, |b| b.bindings.len())
|
||||
+ original
|
||||
.cases
|
||||
.iter()
|
||||
.map(|c| c.bindings.len())
|
||||
.sum::<usize>();
|
||||
let (a, b) = diff(&initial, &fixed_source);
|
||||
let (c, d) = diff(&initial, &threshold_source);
|
||||
writeln!(
|
||||
report,
|
||||
"| {name} | {} | {} | {rows} | {a}/{b} | {c}/{d} | 4 | 1 |",
|
||||
initial.lines().count(),
|
||||
initial.len()
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
// Negative controls establish that source locality and isolation differ.
|
||||
let mut d = fixture('D', false);
|
||||
change_fixed(&mut d, false);
|
||||
validate(&d)?;
|
||||
assert_total(&d, 0, 21950);
|
||||
assert_total(&d, 19, 23175);
|
||||
checks += 2;
|
||||
write_fixture(dir, "D-inline-fixed-one-only", &d)?;
|
||||
let b = fixture('B', false);
|
||||
let baseline_case = Case {
|
||||
name: "wrong_baseline_context".into(),
|
||||
base: None,
|
||||
bindings: b.baseline.as_ref().unwrap().bindings.clone(),
|
||||
};
|
||||
assert_eq!(
|
||||
evaluate(&b, &baseline_case, "total")?.value,
|
||||
Value::Int(21750)
|
||||
);
|
||||
assert_total(&b, 19, 23175);
|
||||
checks += 2;
|
||||
let mut explicit = fixture('B', false);
|
||||
explicit
|
||||
.baseline
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.bindings
|
||||
.insert("varying01".into(), Expr::Missing);
|
||||
explicit.cases[19].bindings.remove("varying01");
|
||||
validate(&explicit)?;
|
||||
assert!(matches!(
|
||||
evaluate(&explicit, &explicit.cases[19], "total")?.value,
|
||||
Value::Missing(_)
|
||||
));
|
||||
checks += 1;
|
||||
let mut chained = fixture('B', false);
|
||||
chained.cases[1].base = Some("case01".into());
|
||||
assert!(validate(&chained).unwrap_err().contains("case-to-case"));
|
||||
checks += 1;
|
||||
let mut capture = fixture('D', true);
|
||||
capture.functions.get_mut("budget_formula").unwrap().body = Expr::Ref("fixed01".into());
|
||||
assert!(validate(&capture).unwrap_err().contains("captures"));
|
||||
checks += 1;
|
||||
let mut recursive = fixture('D', true);
|
||||
recursive.functions.get_mut("budget_formula").unwrap().body =
|
||||
Expr::Call("budget_formula".into(), vec![Expr::Ref("result".into())]);
|
||||
assert!(validate(&recursive).unwrap_err().contains("cycle"));
|
||||
checks += 1;
|
||||
writeln!(report,"\nExecuted assertions: {checks}. All passed. Every accepted fixture was parsed, validated, rendered and parsed back with exact experiment AST equality. Rejected cases were checked separately.\n").unwrap();
|
||||
report.push_str("Negative controls: D changing one repeated fixed input changes only that case; B evaluating inherited formulas against baseline inputs instead of effective case inputs gives 21,750 instead of case20's 23,175. A shallow B case copy is not frozen; factored D also needs its definition environment snapshotted. Clearing a B override restores its named baseline value, including an explicitly missing baseline. A deleted D binding row rejects. Baseline chains, recursive definitions and project captures inside pure functions reject.\n");
|
||||
std::fs::write(dir.join("RESULTS.md"), &report).map_err(|e| e.to_string())?;
|
||||
println!("{report}");
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,671 @@
|
||||
{
|
||||
"reviewer": "Codex-9e8r6t /root/palette_finish",
|
||||
"started_utc": "2026-09-11T09:18:19.944507+00:00",
|
||||
"finished_utc": "2026-09-11T09:18:23.213965+00:00",
|
||||
"scope": "Frozen checkpoints only; no live work/profile/UI access or compilation.",
|
||||
"artifacts": [
|
||||
{
|
||||
"path": ".local/behavior-creation-oracle-20260911/behavior-creation-contract-v2.exe",
|
||||
"bytes": 20757504,
|
||||
"sha256": "056eefd444924916bf6ac3c4897ab2ff4a8a4bb892e1dbc16adad32e59bf6a36"
|
||||
},
|
||||
{
|
||||
"path": "harness/behavior-creation-contract.rs",
|
||||
"bytes": 23982,
|
||||
"sha256": "51ab2ffa1826dd949b79f3a9690042a2f9e38daf9ed91517cade3861482117ba"
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/behavior-creation-native-20260911/verify.py",
|
||||
"bytes": 5348,
|
||||
"sha256": "4921c4615071240c3ac3bbe1223c0db3bf472fe5e3eec2ae5dac12f95b1674df"
|
||||
},
|
||||
{
|
||||
"path": ".local/native-qa/behavior-20260911-v4/bin/arcadia-studio.exe",
|
||||
"bytes": 111163904,
|
||||
"sha256": "d4d9c2bd937e301ff1009d7cb93a34ee5aa6dfc837ff7e87d468c85371b20c31"
|
||||
},
|
||||
{
|
||||
"path": ".local/native-qa/behavior-20260911-v4/bin/libunwind.dll",
|
||||
"bytes": 190976,
|
||||
"sha256": "f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4"
|
||||
},
|
||||
{
|
||||
"path": ".local/native-qa/behavior-20260911-v4/bin/manifest.json",
|
||||
"bytes": 41579,
|
||||
"sha256": "e0470afae0ae21941ac6da63fadfb3c70d0ce646317c65fafa5c186e94f18fe2"
|
||||
},
|
||||
{
|
||||
"path": ".local/native-qa/behavior-20260911-v4/baseline-source-manifest.json",
|
||||
"bytes": 927,
|
||||
"sha256": "7b5bcd66739ad9f3ddaa3a50db7a4bba3be144d13d161e1db85d2d43f3038259"
|
||||
},
|
||||
{
|
||||
"path": ".local/native-qa/behavior-20260911-v4/cancel-save-verification.json",
|
||||
"bytes": 1439,
|
||||
"sha256": "5fb79d14a000fa0ecc046fba64bcd2846d3bc2eb8708a88e334db0ede4ce16a1"
|
||||
},
|
||||
{
|
||||
"path": ".local/native-qa/behavior-20260911-v4/scenario-empty-saved.png",
|
||||
"bytes": 90419,
|
||||
"sha256": "b03fb33d3f4cf1e5f95e7ac4198a4dee30ffd9af5e250d481e0d5dc2a2a0f90c"
|
||||
},
|
||||
{
|
||||
"path": ".local/native-qa/behavior-20260911-v4/machine-created-saved.png",
|
||||
"bytes": 98897,
|
||||
"sha256": "1e7af3d6219c430eaafb0de5afd8df4c51b4efeb80de3f642084188633dcb65b"
|
||||
}
|
||||
],
|
||||
"checkpoints": {
|
||||
"baseline": {
|
||||
"path": ".local/native-qa/palette-20260910-v3/checkpoint-transition-reopen",
|
||||
"files": {
|
||||
"oa/activities/activity_assess_incident.arcadia": {
|
||||
"bytes": 376,
|
||||
"sha256": "ae41d7c20b062b39c2edab7fb890a3d88a9ce18fd8d03018ef2d6f95eeff62e8"
|
||||
},
|
||||
"oa/participants.arcadia": {
|
||||
"bytes": 403,
|
||||
"sha256": "094cab67f34b64902942c10a35bbcd0f760886d862588a0816d6ceb57bacf51b"
|
||||
},
|
||||
"project.arcadia": {
|
||||
"bytes": 4844,
|
||||
"sha256": "b35a3f63f223cc4fe2760d6308e87b016ed1eadfbef0a3a4bd051b52c9080958"
|
||||
},
|
||||
"rationale.arcadia": {
|
||||
"bytes": 57,
|
||||
"sha256": "c01867c9dc0d40726853119fac3c6ad24a40d8d362a93a8b9d20df04ec2e273f"
|
||||
},
|
||||
"sa/participants.arcadia": {
|
||||
"bytes": 124,
|
||||
"sha256": "fb975b83d3ec164c38936bf27fe7d2021cc46805574902a534744b040f7d3ae8"
|
||||
},
|
||||
"traceability.arcadia": {
|
||||
"bytes": 395,
|
||||
"sha256": "f778acaaa85de0940cedcd9514cd803032a11e782d859454203235febb73e5f0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"process": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-process",
|
||||
"files": {
|
||||
"oa/activities/activity_assess_incident.arcadia": {
|
||||
"bytes": 376,
|
||||
"sha256": "ae41d7c20b062b39c2edab7fb890a3d88a9ce18fd8d03018ef2d6f95eeff62e8"
|
||||
},
|
||||
"oa/behavior/process_coordinate_rescue.arcadia": {
|
||||
"bytes": 149,
|
||||
"sha256": "8f393d1cee5e7eed5e41af14539b1fe7ae1c7300dd9383ca0ef1ee17254b6253"
|
||||
},
|
||||
"oa/participants.arcadia": {
|
||||
"bytes": 403,
|
||||
"sha256": "094cab67f34b64902942c10a35bbcd0f760886d862588a0816d6ceb57bacf51b"
|
||||
},
|
||||
"project.arcadia": {
|
||||
"bytes": 4906,
|
||||
"sha256": "961d99c287dd7728c8ece8b20ca1a7965ac2d0581b3eaedf11a2b243975b0ea8"
|
||||
},
|
||||
"rationale.arcadia": {
|
||||
"bytes": 57,
|
||||
"sha256": "c01867c9dc0d40726853119fac3c6ad24a40d8d362a93a8b9d20df04ec2e273f"
|
||||
},
|
||||
"sa/participants.arcadia": {
|
||||
"bytes": 124,
|
||||
"sha256": "fb975b83d3ec164c38936bf27fe7d2021cc46805574902a534744b040f7d3ae8"
|
||||
},
|
||||
"traceability.arcadia": {
|
||||
"bytes": 395,
|
||||
"sha256": "f778acaaa85de0940cedcd9514cd803032a11e782d859454203235febb73e5f0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"process-undo": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-process-undo",
|
||||
"files": {
|
||||
"oa/activities/activity_assess_incident.arcadia": {
|
||||
"bytes": 376,
|
||||
"sha256": "ae41d7c20b062b39c2edab7fb890a3d88a9ce18fd8d03018ef2d6f95eeff62e8"
|
||||
},
|
||||
"oa/behavior/process_coordinate_rescue.arcadia": {
|
||||
"bytes": 149,
|
||||
"sha256": "8f393d1cee5e7eed5e41af14539b1fe7ae1c7300dd9383ca0ef1ee17254b6253"
|
||||
},
|
||||
"oa/participants.arcadia": {
|
||||
"bytes": 403,
|
||||
"sha256": "094cab67f34b64902942c10a35bbcd0f760886d862588a0816d6ceb57bacf51b"
|
||||
},
|
||||
"project.arcadia": {
|
||||
"bytes": 4844,
|
||||
"sha256": "b35a3f63f223cc4fe2760d6308e87b016ed1eadfbef0a3a4bd051b52c9080958"
|
||||
},
|
||||
"rationale.arcadia": {
|
||||
"bytes": 57,
|
||||
"sha256": "c01867c9dc0d40726853119fac3c6ad24a40d8d362a93a8b9d20df04ec2e273f"
|
||||
},
|
||||
"sa/participants.arcadia": {
|
||||
"bytes": 124,
|
||||
"sha256": "fb975b83d3ec164c38936bf27fe7d2021cc46805574902a534744b040f7d3ae8"
|
||||
},
|
||||
"traceability.arcadia": {
|
||||
"bytes": 395,
|
||||
"sha256": "f778acaaa85de0940cedcd9514cd803032a11e782d859454203235febb73e5f0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"process-redo": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-process-redo",
|
||||
"files": {
|
||||
"oa/activities/activity_assess_incident.arcadia": {
|
||||
"bytes": 376,
|
||||
"sha256": "ae41d7c20b062b39c2edab7fb890a3d88a9ce18fd8d03018ef2d6f95eeff62e8"
|
||||
},
|
||||
"oa/behavior/process_coordinate_rescue.arcadia": {
|
||||
"bytes": 149,
|
||||
"sha256": "8f393d1cee5e7eed5e41af14539b1fe7ae1c7300dd9383ca0ef1ee17254b6253"
|
||||
},
|
||||
"oa/participants.arcadia": {
|
||||
"bytes": 403,
|
||||
"sha256": "094cab67f34b64902942c10a35bbcd0f760886d862588a0816d6ceb57bacf51b"
|
||||
},
|
||||
"project.arcadia": {
|
||||
"bytes": 4906,
|
||||
"sha256": "961d99c287dd7728c8ece8b20ca1a7965ac2d0581b3eaedf11a2b243975b0ea8"
|
||||
},
|
||||
"rationale.arcadia": {
|
||||
"bytes": 57,
|
||||
"sha256": "c01867c9dc0d40726853119fac3c6ad24a40d8d362a93a8b9d20df04ec2e273f"
|
||||
},
|
||||
"sa/participants.arcadia": {
|
||||
"bytes": 124,
|
||||
"sha256": "fb975b83d3ec164c38936bf27fe7d2021cc46805574902a534744b040f7d3ae8"
|
||||
},
|
||||
"traceability.arcadia": {
|
||||
"bytes": 395,
|
||||
"sha256": "f778acaaa85de0940cedcd9514cd803032a11e782d859454203235febb73e5f0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scenario": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-scenario",
|
||||
"files": {
|
||||
"oa/activities/activity_assess_incident.arcadia": {
|
||||
"bytes": 376,
|
||||
"sha256": "ae41d7c20b062b39c2edab7fb890a3d88a9ce18fd8d03018ef2d6f95eeff62e8"
|
||||
},
|
||||
"oa/behavior/process_coordinate_rescue.arcadia": {
|
||||
"bytes": 149,
|
||||
"sha256": "8f393d1cee5e7eed5e41af14539b1fe7ae1c7300dd9383ca0ef1ee17254b6253"
|
||||
},
|
||||
"oa/behavior/scenario_rescue_coordination_conversation.arcadia": {
|
||||
"bytes": 284,
|
||||
"sha256": "8eed98446dfb4f4dbd5261f550ce8cae1a21d638975d91b32baaf6f309daba98"
|
||||
},
|
||||
"oa/participants.arcadia": {
|
||||
"bytes": 403,
|
||||
"sha256": "094cab67f34b64902942c10a35bbcd0f760886d862588a0816d6ceb57bacf51b"
|
||||
},
|
||||
"project.arcadia": {
|
||||
"bytes": 4984,
|
||||
"sha256": "1fa6dd826fd53ccdf3c93cfdcdd657487e344313d76dabe0c438e08128851ee6"
|
||||
},
|
||||
"rationale.arcadia": {
|
||||
"bytes": 57,
|
||||
"sha256": "c01867c9dc0d40726853119fac3c6ad24a40d8d362a93a8b9d20df04ec2e273f"
|
||||
},
|
||||
"sa/participants.arcadia": {
|
||||
"bytes": 124,
|
||||
"sha256": "fb975b83d3ec164c38936bf27fe7d2021cc46805574902a534744b040f7d3ae8"
|
||||
},
|
||||
"traceability.arcadia": {
|
||||
"bytes": 395,
|
||||
"sha256": "f778acaaa85de0940cedcd9514cd803032a11e782d859454203235febb73e5f0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scenario-undo": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-scenario-undo",
|
||||
"files": {
|
||||
"oa/activities/activity_assess_incident.arcadia": {
|
||||
"bytes": 376,
|
||||
"sha256": "ae41d7c20b062b39c2edab7fb890a3d88a9ce18fd8d03018ef2d6f95eeff62e8"
|
||||
},
|
||||
"oa/behavior/process_coordinate_rescue.arcadia": {
|
||||
"bytes": 149,
|
||||
"sha256": "8f393d1cee5e7eed5e41af14539b1fe7ae1c7300dd9383ca0ef1ee17254b6253"
|
||||
},
|
||||
"oa/behavior/scenario_rescue_coordination_conversation.arcadia": {
|
||||
"bytes": 284,
|
||||
"sha256": "8eed98446dfb4f4dbd5261f550ce8cae1a21d638975d91b32baaf6f309daba98"
|
||||
},
|
||||
"oa/participants.arcadia": {
|
||||
"bytes": 403,
|
||||
"sha256": "094cab67f34b64902942c10a35bbcd0f760886d862588a0816d6ceb57bacf51b"
|
||||
},
|
||||
"project.arcadia": {
|
||||
"bytes": 4906,
|
||||
"sha256": "961d99c287dd7728c8ece8b20ca1a7965ac2d0581b3eaedf11a2b243975b0ea8"
|
||||
},
|
||||
"rationale.arcadia": {
|
||||
"bytes": 57,
|
||||
"sha256": "c01867c9dc0d40726853119fac3c6ad24a40d8d362a93a8b9d20df04ec2e273f"
|
||||
},
|
||||
"sa/participants.arcadia": {
|
||||
"bytes": 124,
|
||||
"sha256": "fb975b83d3ec164c38936bf27fe7d2021cc46805574902a534744b040f7d3ae8"
|
||||
},
|
||||
"traceability.arcadia": {
|
||||
"bytes": 395,
|
||||
"sha256": "f778acaaa85de0940cedcd9514cd803032a11e782d859454203235febb73e5f0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scenario-redo": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-scenario-redo",
|
||||
"files": {
|
||||
"oa/activities/activity_assess_incident.arcadia": {
|
||||
"bytes": 376,
|
||||
"sha256": "ae41d7c20b062b39c2edab7fb890a3d88a9ce18fd8d03018ef2d6f95eeff62e8"
|
||||
},
|
||||
"oa/behavior/process_coordinate_rescue.arcadia": {
|
||||
"bytes": 149,
|
||||
"sha256": "8f393d1cee5e7eed5e41af14539b1fe7ae1c7300dd9383ca0ef1ee17254b6253"
|
||||
},
|
||||
"oa/behavior/scenario_rescue_coordination_conversation.arcadia": {
|
||||
"bytes": 284,
|
||||
"sha256": "8eed98446dfb4f4dbd5261f550ce8cae1a21d638975d91b32baaf6f309daba98"
|
||||
},
|
||||
"oa/participants.arcadia": {
|
||||
"bytes": 403,
|
||||
"sha256": "094cab67f34b64902942c10a35bbcd0f760886d862588a0816d6ceb57bacf51b"
|
||||
},
|
||||
"project.arcadia": {
|
||||
"bytes": 4984,
|
||||
"sha256": "1fa6dd826fd53ccdf3c93cfdcdd657487e344313d76dabe0c438e08128851ee6"
|
||||
},
|
||||
"rationale.arcadia": {
|
||||
"bytes": 57,
|
||||
"sha256": "c01867c9dc0d40726853119fac3c6ad24a40d8d362a93a8b9d20df04ec2e273f"
|
||||
},
|
||||
"sa/participants.arcadia": {
|
||||
"bytes": 124,
|
||||
"sha256": "fb975b83d3ec164c38936bf27fe7d2021cc46805574902a534744b040f7d3ae8"
|
||||
},
|
||||
"traceability.arcadia": {
|
||||
"bytes": 395,
|
||||
"sha256": "f778acaaa85de0940cedcd9514cd803032a11e782d859454203235febb73e5f0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"machine": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-machine",
|
||||
"files": {
|
||||
"oa/activities/activity_assess_incident.arcadia": {
|
||||
"bytes": 376,
|
||||
"sha256": "ae41d7c20b062b39c2edab7fb890a3d88a9ce18fd8d03018ef2d6f95eeff62e8"
|
||||
},
|
||||
"oa/behavior/process_coordinate_rescue.arcadia": {
|
||||
"bytes": 149,
|
||||
"sha256": "8f393d1cee5e7eed5e41af14539b1fe7ae1c7300dd9383ca0ef1ee17254b6253"
|
||||
},
|
||||
"oa/behavior/scenario_rescue_coordination_conversation.arcadia": {
|
||||
"bytes": 284,
|
||||
"sha256": "8eed98446dfb4f4dbd5261f550ce8cae1a21d638975d91b32baaf6f309daba98"
|
||||
},
|
||||
"oa/participants.arcadia": {
|
||||
"bytes": 403,
|
||||
"sha256": "094cab67f34b64902942c10a35bbcd0f760886d862588a0816d6ceb57bacf51b"
|
||||
},
|
||||
"project.arcadia": {
|
||||
"bytes": 5057,
|
||||
"sha256": "59b61b321b0cf4817c1ff9276fa59deafc09518765e761a2ebd9e5602afab9cb"
|
||||
},
|
||||
"rationale.arcadia": {
|
||||
"bytes": 57,
|
||||
"sha256": "c01867c9dc0d40726853119fac3c6ad24a40d8d362a93a8b9d20df04ec2e273f"
|
||||
},
|
||||
"sa/participants.arcadia": {
|
||||
"bytes": 285,
|
||||
"sha256": "9b77b88f66e4e80540ae33b83f1af1f3edcd37dafb98c33a861d4c34c66d2559"
|
||||
},
|
||||
"traceability.arcadia": {
|
||||
"bytes": 395,
|
||||
"sha256": "f778acaaa85de0940cedcd9514cd803032a11e782d859454203235febb73e5f0"
|
||||
},
|
||||
"views/diagram_rescue_team_operating_modes_states.arcadia": {
|
||||
"bytes": 842,
|
||||
"sha256": "498eab80da7ac4523883622a0d008b9033a1305e5bb5f168b8e9d4c41dde62de"
|
||||
}
|
||||
}
|
||||
},
|
||||
"machine-undo": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-machine-undo",
|
||||
"files": {
|
||||
"oa/activities/activity_assess_incident.arcadia": {
|
||||
"bytes": 376,
|
||||
"sha256": "ae41d7c20b062b39c2edab7fb890a3d88a9ce18fd8d03018ef2d6f95eeff62e8"
|
||||
},
|
||||
"oa/behavior/process_coordinate_rescue.arcadia": {
|
||||
"bytes": 149,
|
||||
"sha256": "8f393d1cee5e7eed5e41af14539b1fe7ae1c7300dd9383ca0ef1ee17254b6253"
|
||||
},
|
||||
"oa/behavior/scenario_rescue_coordination_conversation.arcadia": {
|
||||
"bytes": 284,
|
||||
"sha256": "8eed98446dfb4f4dbd5261f550ce8cae1a21d638975d91b32baaf6f309daba98"
|
||||
},
|
||||
"oa/participants.arcadia": {
|
||||
"bytes": 403,
|
||||
"sha256": "094cab67f34b64902942c10a35bbcd0f760886d862588a0816d6ceb57bacf51b"
|
||||
},
|
||||
"project.arcadia": {
|
||||
"bytes": 4984,
|
||||
"sha256": "1fa6dd826fd53ccdf3c93cfdcdd657487e344313d76dabe0c438e08128851ee6"
|
||||
},
|
||||
"rationale.arcadia": {
|
||||
"bytes": 57,
|
||||
"sha256": "c01867c9dc0d40726853119fac3c6ad24a40d8d362a93a8b9d20df04ec2e273f"
|
||||
},
|
||||
"sa/participants.arcadia": {
|
||||
"bytes": 124,
|
||||
"sha256": "fb975b83d3ec164c38936bf27fe7d2021cc46805574902a534744b040f7d3ae8"
|
||||
},
|
||||
"traceability.arcadia": {
|
||||
"bytes": 395,
|
||||
"sha256": "f778acaaa85de0940cedcd9514cd803032a11e782d859454203235febb73e5f0"
|
||||
},
|
||||
"views/diagram_rescue_team_operating_modes_states.arcadia": {
|
||||
"bytes": 842,
|
||||
"sha256": "498eab80da7ac4523883622a0d008b9033a1305e5bb5f168b8e9d4c41dde62de"
|
||||
}
|
||||
}
|
||||
},
|
||||
"machine-redo": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-machine-redo",
|
||||
"files": {
|
||||
"oa/activities/activity_assess_incident.arcadia": {
|
||||
"bytes": 376,
|
||||
"sha256": "ae41d7c20b062b39c2edab7fb890a3d88a9ce18fd8d03018ef2d6f95eeff62e8"
|
||||
},
|
||||
"oa/behavior/process_coordinate_rescue.arcadia": {
|
||||
"bytes": 149,
|
||||
"sha256": "8f393d1cee5e7eed5e41af14539b1fe7ae1c7300dd9383ca0ef1ee17254b6253"
|
||||
},
|
||||
"oa/behavior/scenario_rescue_coordination_conversation.arcadia": {
|
||||
"bytes": 284,
|
||||
"sha256": "8eed98446dfb4f4dbd5261f550ce8cae1a21d638975d91b32baaf6f309daba98"
|
||||
},
|
||||
"oa/participants.arcadia": {
|
||||
"bytes": 403,
|
||||
"sha256": "094cab67f34b64902942c10a35bbcd0f760886d862588a0816d6ceb57bacf51b"
|
||||
},
|
||||
"project.arcadia": {
|
||||
"bytes": 5057,
|
||||
"sha256": "59b61b321b0cf4817c1ff9276fa59deafc09518765e761a2ebd9e5602afab9cb"
|
||||
},
|
||||
"rationale.arcadia": {
|
||||
"bytes": 57,
|
||||
"sha256": "c01867c9dc0d40726853119fac3c6ad24a40d8d362a93a8b9d20df04ec2e273f"
|
||||
},
|
||||
"sa/participants.arcadia": {
|
||||
"bytes": 285,
|
||||
"sha256": "9b77b88f66e4e80540ae33b83f1af1f3edcd37dafb98c33a861d4c34c66d2559"
|
||||
},
|
||||
"traceability.arcadia": {
|
||||
"bytes": 395,
|
||||
"sha256": "f778acaaa85de0940cedcd9514cd803032a11e782d859454203235febb73e5f0"
|
||||
},
|
||||
"views/diagram_rescue_team_operating_modes_states.arcadia": {
|
||||
"bytes": 842,
|
||||
"sha256": "498eab80da7ac4523883622a0d008b9033a1305e5bb5f168b8e9d4c41dde62de"
|
||||
}
|
||||
}
|
||||
},
|
||||
"creation-reopen": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-creation-reopen",
|
||||
"files": {
|
||||
"oa/activities/activity_assess_incident.arcadia": {
|
||||
"bytes": 376,
|
||||
"sha256": "ae41d7c20b062b39c2edab7fb890a3d88a9ce18fd8d03018ef2d6f95eeff62e8"
|
||||
},
|
||||
"oa/behavior/process_coordinate_rescue.arcadia": {
|
||||
"bytes": 149,
|
||||
"sha256": "8f393d1cee5e7eed5e41af14539b1fe7ae1c7300dd9383ca0ef1ee17254b6253"
|
||||
},
|
||||
"oa/behavior/scenario_rescue_coordination_conversation.arcadia": {
|
||||
"bytes": 284,
|
||||
"sha256": "8eed98446dfb4f4dbd5261f550ce8cae1a21d638975d91b32baaf6f309daba98"
|
||||
},
|
||||
"oa/participants.arcadia": {
|
||||
"bytes": 403,
|
||||
"sha256": "094cab67f34b64902942c10a35bbcd0f760886d862588a0816d6ceb57bacf51b"
|
||||
},
|
||||
"project.arcadia": {
|
||||
"bytes": 5057,
|
||||
"sha256": "59b61b321b0cf4817c1ff9276fa59deafc09518765e761a2ebd9e5602afab9cb"
|
||||
},
|
||||
"rationale.arcadia": {
|
||||
"bytes": 57,
|
||||
"sha256": "c01867c9dc0d40726853119fac3c6ad24a40d8d362a93a8b9d20df04ec2e273f"
|
||||
},
|
||||
"sa/participants.arcadia": {
|
||||
"bytes": 285,
|
||||
"sha256": "9b77b88f66e4e80540ae33b83f1af1f3edcd37dafb98c33a861d4c34c66d2559"
|
||||
},
|
||||
"traceability.arcadia": {
|
||||
"bytes": 395,
|
||||
"sha256": "f778acaaa85de0940cedcd9514cd803032a11e782d859454203235febb73e5f0"
|
||||
},
|
||||
"views/diagram_rescue_team_operating_modes_states.arcadia": {
|
||||
"bytes": 842,
|
||||
"sha256": "498eab80da7ac4523883622a0d008b9033a1305e5bb5f168b8e9d4c41dde62de"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"label": "self-check",
|
||||
"command_argv": [
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\behavior-creation-oracle-20260911\\behavior-creation-contract-v2.exe",
|
||||
"self-check",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\palette-20260910-v3\\checkpoint-transition-reopen\\project.arcadia"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"elapsed_seconds": 0.967588,
|
||||
"stdout": "PASS self-check Process: full source fixed point; false metadata, unrelated edits and source-only drift rejected\nPASS self-check Scenario: full source fixed point; false metadata, unrelated edits and source-only drift rejected\nPREDICT first view: Rescue team operating modes \u00b7 states; machine-root position Position { x: 60.0, y: 60.0 }, size Size { width: 336.0, height: 104.0 }; 9 exact source files\nPASS self-check Machine: full source fixed point; false metadata, unrelated edits and source-only drift rejected\nPASS explicit Undo disk contract: known detached retention accepted; unknown, changed, missing and reachable drift rejected\nPASS baseline retained unchanged; no native acceptance claimed\n",
|
||||
"stderr": ""
|
||||
},
|
||||
{
|
||||
"label": "baseline",
|
||||
"command_argv": [
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\behavior-creation-oracle-20260911\\behavior-creation-contract-v2.exe",
|
||||
"check",
|
||||
"baseline",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\palette-20260910-v3\\checkpoint-transition-reopen\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\palette-20260910-v3\\checkpoint-transition-reopen\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\palette-20260910-v3\\checkpoint-transition-reopen\\project.arcadia"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"elapsed_seconds": 0.119426,
|
||||
"stdout": "PASS Baseline: complete Project + 6 exact source files; only fresh literal-role UUIDs borrowed\n",
|
||||
"stderr": ""
|
||||
},
|
||||
{
|
||||
"label": "process",
|
||||
"command_argv": [
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\behavior-creation-oracle-20260911\\behavior-creation-contract-v2.exe",
|
||||
"check",
|
||||
"process",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\palette-20260910-v3\\checkpoint-transition-reopen\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-process\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-process\\project.arcadia"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"elapsed_seconds": 0.163785,
|
||||
"stdout": "PASS Process: complete Project + 7 exact source files; only fresh literal-role UUIDs borrowed\n",
|
||||
"stderr": ""
|
||||
},
|
||||
{
|
||||
"label": "process-undo",
|
||||
"command_argv": [
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\behavior-creation-oracle-20260911\\behavior-creation-contract-v2.exe",
|
||||
"same",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\palette-20260910-v3\\checkpoint-transition-reopen\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-process-undo\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-process\\project.arcadia"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"elapsed_seconds": 0.155607,
|
||||
"stdout": "PASS exact checkpoint identity/model/6-file SourceBundle equality; 7 exact disk files (1 known retained)\n",
|
||||
"stderr": ""
|
||||
},
|
||||
{
|
||||
"label": "process-redo",
|
||||
"command_argv": [
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\behavior-creation-oracle-20260911\\behavior-creation-contract-v2.exe",
|
||||
"check",
|
||||
"process",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\palette-20260910-v3\\checkpoint-transition-reopen\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-process-redo\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-process\\project.arcadia"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"elapsed_seconds": 0.187937,
|
||||
"stdout": "PASS Process: complete Project + 7 exact source files; only fresh literal-role UUIDs borrowed\n",
|
||||
"stderr": ""
|
||||
},
|
||||
{
|
||||
"label": "scenario",
|
||||
"command_argv": [
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\behavior-creation-oracle-20260911\\behavior-creation-contract-v2.exe",
|
||||
"check",
|
||||
"scenario",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\palette-20260910-v3\\checkpoint-transition-reopen\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-scenario\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-scenario\\project.arcadia"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"elapsed_seconds": 0.253057,
|
||||
"stdout": "PASS Scenario: complete Project + 8 exact source files; only fresh literal-role UUIDs borrowed\n",
|
||||
"stderr": ""
|
||||
},
|
||||
{
|
||||
"label": "scenario-undo",
|
||||
"command_argv": [
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\behavior-creation-oracle-20260911\\behavior-creation-contract-v2.exe",
|
||||
"same",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-process\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-scenario-undo\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-scenario\\project.arcadia"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"elapsed_seconds": 0.160712,
|
||||
"stdout": "PASS exact checkpoint identity/model/7-file SourceBundle equality; 8 exact disk files (1 known retained)\n",
|
||||
"stderr": ""
|
||||
},
|
||||
{
|
||||
"label": "scenario-redo",
|
||||
"command_argv": [
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\behavior-creation-oracle-20260911\\behavior-creation-contract-v2.exe",
|
||||
"check",
|
||||
"scenario",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\palette-20260910-v3\\checkpoint-transition-reopen\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-scenario-redo\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-scenario\\project.arcadia"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"elapsed_seconds": 0.222,
|
||||
"stdout": "PASS Scenario: complete Project + 8 exact source files; only fresh literal-role UUIDs borrowed\n",
|
||||
"stderr": ""
|
||||
},
|
||||
{
|
||||
"label": "machine",
|
||||
"command_argv": [
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\behavior-creation-oracle-20260911\\behavior-creation-contract-v2.exe",
|
||||
"check",
|
||||
"machine",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\palette-20260910-v3\\checkpoint-transition-reopen\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-machine\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-machine\\project.arcadia"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"elapsed_seconds": 0.266748,
|
||||
"stdout": "PASS Machine: complete Project + 9 exact source files; only fresh literal-role UUIDs borrowed\n",
|
||||
"stderr": ""
|
||||
},
|
||||
{
|
||||
"label": "machine-undo",
|
||||
"command_argv": [
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\behavior-creation-oracle-20260911\\behavior-creation-contract-v2.exe",
|
||||
"same",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-scenario\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-machine-undo\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-machine\\project.arcadia"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"elapsed_seconds": 0.122982,
|
||||
"stdout": "PASS exact checkpoint identity/model/8-file SourceBundle equality; 9 exact disk files (1 known retained)\n",
|
||||
"stderr": ""
|
||||
},
|
||||
{
|
||||
"label": "machine-redo",
|
||||
"command_argv": [
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\behavior-creation-oracle-20260911\\behavior-creation-contract-v2.exe",
|
||||
"check",
|
||||
"machine",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\palette-20260910-v3\\checkpoint-transition-reopen\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-machine-redo\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-machine\\project.arcadia"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"elapsed_seconds": 0.229706,
|
||||
"stdout": "PASS Machine: complete Project + 9 exact source files; only fresh literal-role UUIDs borrowed\n",
|
||||
"stderr": ""
|
||||
},
|
||||
{
|
||||
"label": "reopen-contract",
|
||||
"command_argv": [
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\behavior-creation-oracle-20260911\\behavior-creation-contract-v2.exe",
|
||||
"check",
|
||||
"machine",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\palette-20260910-v3\\checkpoint-transition-reopen\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-creation-reopen\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-machine\\project.arcadia"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"elapsed_seconds": 0.192465,
|
||||
"stdout": "PASS Machine: complete Project + 9 exact source files; only fresh literal-role UUIDs borrowed\n",
|
||||
"stderr": ""
|
||||
},
|
||||
{
|
||||
"label": "reopen-exact-redo",
|
||||
"command_argv": [
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\behavior-creation-oracle-20260911\\behavior-creation-contract-v2.exe",
|
||||
"same",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-machine-redo\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-creation-reopen\\project.arcadia"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"elapsed_seconds": 0.131742,
|
||||
"stdout": "PASS exact checkpoint identity/model/9-file SourceBundle equality; 9 exact disk files (0 known retained)\n",
|
||||
"stderr": ""
|
||||
}
|
||||
],
|
||||
"all_checkpoint_files_unchanged": true,
|
||||
"machine_redo_reopen_inventory_equal": true,
|
||||
"machine_redo_reopen_direct_byte_comparisons": {
|
||||
"oa/activities/activity_assess_incident.arcadia": true,
|
||||
"oa/behavior/process_coordinate_rescue.arcadia": true,
|
||||
"oa/behavior/scenario_rescue_coordination_conversation.arcadia": true,
|
||||
"oa/participants.arcadia": true,
|
||||
"project.arcadia": true,
|
||||
"rationale.arcadia": true,
|
||||
"sa/participants.arcadia": true,
|
||||
"traceability.arcadia": true,
|
||||
"views/diagram_rescue_team_operating_modes_states.arcadia": true
|
||||
},
|
||||
"log": {
|
||||
"path": "harness/experiments/behavior-creation-native-20260911/commands-results.log",
|
||||
"bytes": 9392,
|
||||
"sha256": "f5bfbee185f7064419b1bd9fe33ec6a48526b5bbe3f818c006feb74eddf8e189"
|
||||
},
|
||||
"pass": true
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
{
|
||||
"reviewer": "Codex-9e8r6t /root/palette_finish",
|
||||
"started_utc": "2026-09-11T09:22:33.387447+00:00",
|
||||
"finished_utc": "2026-09-11T09:22:34.502189+00:00",
|
||||
"scope": "Immutable evidence only; native observations supplied separately by root. No live work/profile/UI/Cargo access; no persisted Undo claim.",
|
||||
"artifacts": [
|
||||
{
|
||||
"path": "harness/experiments/behavior-creation-native-20260911/verify-recovery.py",
|
||||
"bytes": 5560,
|
||||
"sha256": "3546b4fe68d3e1a2474015be1b652a665af238e5fcf63e04bac3f25df3d35e90"
|
||||
},
|
||||
{
|
||||
"path": "harness/experiments/behavior-creation-native-20260911/manifest.json",
|
||||
"bytes": 30406,
|
||||
"sha256": "356dfbb27dec04764a4b3f2372602f39953b472baac0a3de3e72acd9c1d76b8a"
|
||||
},
|
||||
{
|
||||
"path": ".local/behavior-creation-oracle-20260911/behavior-creation-contract-v2.exe",
|
||||
"bytes": 20757504,
|
||||
"sha256": "056eefd444924916bf6ac3c4897ab2ff4a8a4bb892e1dbc16adad32e59bf6a36"
|
||||
},
|
||||
{
|
||||
"path": ".local/native-qa/behavior-20260911-v4/recovery-evidence/before-interruption.json",
|
||||
"bytes": 27382,
|
||||
"sha256": "0c355aa3c7b9be623d767ff35c4b85cf7b2907eb9bc3be39d619aa610c989692"
|
||||
},
|
||||
{
|
||||
"path": ".local/native-qa/behavior-20260911-v4/recovery-evidence/after-recovery-and-typing.json",
|
||||
"bytes": 27263,
|
||||
"sha256": "fafa7a884275bac09afab365f8ab0063f998ca56c5ffc70be596db6f78a10103"
|
||||
},
|
||||
{
|
||||
"path": ".local/native-qa/behavior-20260911-v4/recovery-evidence/restored-draft.png",
|
||||
"bytes": 113820,
|
||||
"sha256": "8ae0fe8957d5a935b6e2605a6ffb68934bd0e8840f3594c43e63865ab89d03c9"
|
||||
},
|
||||
{
|
||||
"path": ".local/native-qa/behavior-20260911-v4/recovery-launch.json",
|
||||
"bytes": 812,
|
||||
"sha256": "641ff05ea50d551a51e642f728650cf254dbf131cfb2f585865db436a8877396"
|
||||
},
|
||||
{
|
||||
"path": ".local/native-qa/behavior-20260911-v4/bin/arcadia-studio.exe",
|
||||
"bytes": 111163904,
|
||||
"sha256": "d4d9c2bd937e301ff1009d7cb93a34ee5aa6dfc837ff7e87d468c85371b20c31"
|
||||
}
|
||||
],
|
||||
"input_file_inventories": {
|
||||
".local\\native-qa\\behavior-20260911-v4\\checkpoint-creation-reopen": {
|
||||
"oa/activities/activity_assess_incident.arcadia": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-creation-reopen/oa/activities/activity_assess_incident.arcadia",
|
||||
"bytes": 376,
|
||||
"sha256": "ae41d7c20b062b39c2edab7fb890a3d88a9ce18fd8d03018ef2d6f95eeff62e8"
|
||||
},
|
||||
"oa/behavior/process_coordinate_rescue.arcadia": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-creation-reopen/oa/behavior/process_coordinate_rescue.arcadia",
|
||||
"bytes": 149,
|
||||
"sha256": "8f393d1cee5e7eed5e41af14539b1fe7ae1c7300dd9383ca0ef1ee17254b6253"
|
||||
},
|
||||
"oa/behavior/scenario_rescue_coordination_conversation.arcadia": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-creation-reopen/oa/behavior/scenario_rescue_coordination_conversation.arcadia",
|
||||
"bytes": 284,
|
||||
"sha256": "8eed98446dfb4f4dbd5261f550ce8cae1a21d638975d91b32baaf6f309daba98"
|
||||
},
|
||||
"oa/participants.arcadia": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-creation-reopen/oa/participants.arcadia",
|
||||
"bytes": 403,
|
||||
"sha256": "094cab67f34b64902942c10a35bbcd0f760886d862588a0816d6ceb57bacf51b"
|
||||
},
|
||||
"project.arcadia": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-creation-reopen/project.arcadia",
|
||||
"bytes": 5057,
|
||||
"sha256": "59b61b321b0cf4817c1ff9276fa59deafc09518765e761a2ebd9e5602afab9cb"
|
||||
},
|
||||
"rationale.arcadia": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-creation-reopen/rationale.arcadia",
|
||||
"bytes": 57,
|
||||
"sha256": "c01867c9dc0d40726853119fac3c6ad24a40d8d362a93a8b9d20df04ec2e273f"
|
||||
},
|
||||
"sa/participants.arcadia": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-creation-reopen/sa/participants.arcadia",
|
||||
"bytes": 285,
|
||||
"sha256": "9b77b88f66e4e80540ae33b83f1af1f3edcd37dafb98c33a861d4c34c66d2559"
|
||||
},
|
||||
"traceability.arcadia": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-creation-reopen/traceability.arcadia",
|
||||
"bytes": 395,
|
||||
"sha256": "f778acaaa85de0940cedcd9514cd803032a11e782d859454203235febb73e5f0"
|
||||
},
|
||||
"views/diagram_rescue_team_operating_modes_states.arcadia": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-creation-reopen/views/diagram_rescue_team_operating_modes_states.arcadia",
|
||||
"bytes": 842,
|
||||
"sha256": "498eab80da7ac4523883622a0d008b9033a1305e5bb5f168b8e9d4c41dde62de"
|
||||
}
|
||||
},
|
||||
".local\\native-qa\\behavior-20260911-v4\\checkpoint-recovery-cancel": {
|
||||
"oa/activities/activity_assess_incident.arcadia": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-recovery-cancel/oa/activities/activity_assess_incident.arcadia",
|
||||
"bytes": 376,
|
||||
"sha256": "ae41d7c20b062b39c2edab7fb890a3d88a9ce18fd8d03018ef2d6f95eeff62e8"
|
||||
},
|
||||
"oa/behavior/process_coordinate_rescue.arcadia": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-recovery-cancel/oa/behavior/process_coordinate_rescue.arcadia",
|
||||
"bytes": 149,
|
||||
"sha256": "8f393d1cee5e7eed5e41af14539b1fe7ae1c7300dd9383ca0ef1ee17254b6253"
|
||||
},
|
||||
"oa/behavior/scenario_rescue_coordination_conversation.arcadia": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-recovery-cancel/oa/behavior/scenario_rescue_coordination_conversation.arcadia",
|
||||
"bytes": 284,
|
||||
"sha256": "8eed98446dfb4f4dbd5261f550ce8cae1a21d638975d91b32baaf6f309daba98"
|
||||
},
|
||||
"oa/participants.arcadia": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-recovery-cancel/oa/participants.arcadia",
|
||||
"bytes": 403,
|
||||
"sha256": "094cab67f34b64902942c10a35bbcd0f760886d862588a0816d6ceb57bacf51b"
|
||||
},
|
||||
"project.arcadia": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-recovery-cancel/project.arcadia",
|
||||
"bytes": 5057,
|
||||
"sha256": "59b61b321b0cf4817c1ff9276fa59deafc09518765e761a2ebd9e5602afab9cb"
|
||||
},
|
||||
"rationale.arcadia": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-recovery-cancel/rationale.arcadia",
|
||||
"bytes": 57,
|
||||
"sha256": "c01867c9dc0d40726853119fac3c6ad24a40d8d362a93a8b9d20df04ec2e273f"
|
||||
},
|
||||
"sa/participants.arcadia": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-recovery-cancel/sa/participants.arcadia",
|
||||
"bytes": 285,
|
||||
"sha256": "9b77b88f66e4e80540ae33b83f1af1f3edcd37dafb98c33a861d4c34c66d2559"
|
||||
},
|
||||
"traceability.arcadia": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-recovery-cancel/traceability.arcadia",
|
||||
"bytes": 395,
|
||||
"sha256": "f778acaaa85de0940cedcd9514cd803032a11e782d859454203235febb73e5f0"
|
||||
},
|
||||
"views/diagram_rescue_team_operating_modes_states.arcadia": {
|
||||
"path": ".local/native-qa/behavior-20260911-v4/checkpoint-recovery-cancel/views/diagram_rescue_team_operating_modes_states.arcadia",
|
||||
"bytes": 842,
|
||||
"sha256": "498eab80da7ac4523883622a0d008b9033a1305e5bb5f168b8e9d4c41dde62de"
|
||||
}
|
||||
}
|
||||
},
|
||||
"checks": {
|
||||
"literal_name_before": true,
|
||||
"literal_name_after": true,
|
||||
"sa_machine": true,
|
||||
"selected_exact_system_actor": true,
|
||||
"first_view_selected": true,
|
||||
"complete_behavior_equal_except_deliberate_suffix": true,
|
||||
"accepted_exact_json_equality": true,
|
||||
"workspace_exact_json_equality": true,
|
||||
"property_exact_json_equality": true,
|
||||
"workflow_exact_json_equality": true,
|
||||
"selected_exact_json_equality": true,
|
||||
"reserved_ids_unchanged": true,
|
||||
"complete_file_inventory_equal": true,
|
||||
"complete_file_bytes_equal": true,
|
||||
"accepted_snapshot_exactly_matches_saved_checkpoint": true,
|
||||
"reserved_objects_not_committed": true,
|
||||
"oracle_same_pass": true,
|
||||
"input_files_unchanged": true,
|
||||
"evidence_and_prior_manifest_unchanged": true
|
||||
},
|
||||
"reserved_ids": {
|
||||
"element": "c6f559f5-401e-4e0d-af93-91faee68d3f5",
|
||||
"diagram": "11ad8f0a-73aa-416a-bbeb-9b886bf39854",
|
||||
"placement": "171f7d7e-3a8f-4200-86f6-a9e2efd87610"
|
||||
},
|
||||
"reopen_recovery_cancel_direct_byte_comparisons": {
|
||||
"views/diagram_rescue_team_operating_modes_states.arcadia": true,
|
||||
"traceability.arcadia": true,
|
||||
"oa/activities/activity_assess_incident.arcadia": true,
|
||||
"sa/participants.arcadia": true,
|
||||
"rationale.arcadia": true,
|
||||
"oa/behavior/process_coordinate_rescue.arcadia": true,
|
||||
"project.arcadia": true,
|
||||
"oa/participants.arcadia": true,
|
||||
"oa/behavior/scenario_rescue_coordination_conversation.arcadia": true
|
||||
},
|
||||
"comparison_origin": "checkpoint-creation-reopen",
|
||||
"command": {
|
||||
"command_argv": [
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\behavior-creation-oracle-20260911\\behavior-creation-contract-v2.exe",
|
||||
"same",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-creation-reopen\\project.arcadia",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\behavior-20260911-v4\\checkpoint-recovery-cancel\\project.arcadia"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"elapsed_seconds": 0.147209,
|
||||
"stdout": "PASS exact checkpoint identity/model/9-file SourceBundle equality; 9 exact disk files (0 known retained)\n",
|
||||
"stderr": ""
|
||||
},
|
||||
"log": {
|
||||
"path": "harness/experiments/behavior-creation-native-20260911/recovery-commands-results.log",
|
||||
"bytes": 641,
|
||||
"sha256": "1dc3e89aa46660bfad796e7b6bf821bebd7e5bc5a7902a826021d461a3e74c68"
|
||||
},
|
||||
"pass": true
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
"""Verify immutable recovery evidence; never access live profile/work or UI."""
|
||||
from pathlib import Path
|
||||
import copy
|
||||
import datetime
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
OUT = Path(__file__).resolve().parent
|
||||
STAGE = ROOT / '.local/native-qa/behavior-20260911-v4'
|
||||
EVIDENCE = STAGE / 'recovery-evidence'
|
||||
EXPECTED = STAGE / 'checkpoint-creation-reopen'
|
||||
ACTUAL = STAGE / 'checkpoint-recovery-cancel'
|
||||
EXE = ROOT / '.local/behavior-creation-oracle-20260911/behavior-creation-contract-v2.exe'
|
||||
|
||||
|
||||
def record(path):
|
||||
data = path.read_bytes()
|
||||
return {'path': path.relative_to(ROOT).as_posix(), 'bytes': len(data),
|
||||
'sha256': hashlib.sha256(data).hexdigest()}
|
||||
|
||||
|
||||
def inventory(directory):
|
||||
return {p.relative_to(directory).as_posix(): record(p)
|
||||
for p in sorted(directory.rglob('*')) if p.is_file()}
|
||||
|
||||
|
||||
artifacts = [record(p) for p in (Path(__file__), OUT / 'manifest.json', EXE,
|
||||
EVIDENCE / 'before-interruption.json', EVIDENCE / 'after-recovery-and-typing.json',
|
||||
EVIDENCE / 'restored-draft.png', STAGE / 'recovery-launch.json', STAGE / 'bin/arcadia-studio.exe')]
|
||||
assert artifacts[1]['sha256'] == '356dfbb27dec04764a4b3f2372602f39953b472baac0a3de3e72acd9c1d76b8a'
|
||||
assert artifacts[2]['sha256'] == '056eefd444924916bf6ac3c4897ab2ff4a8a4bb892e1dbc16adad32e59bf6a36'
|
||||
before = json.loads((EVIDENCE / 'before-interruption.json').read_text(encoding='utf-8-sig'))
|
||||
after = json.loads((EVIDENCE / 'after-recovery-and-typing.json').read_text(encoding='utf-8-sig'))
|
||||
draft = before['behavior']['draft']
|
||||
expected_name = 'Recovery — secours Δ «operating modes»'
|
||||
checks = {'literal_name_before': draft['name'] == expected_name,
|
||||
'literal_name_after': after['behavior']['draft']['name'] == expected_name + ' · resumed',
|
||||
'sa_machine': draft['layer'] == 'Sa' and draft['family'] == 'Machine',
|
||||
'selected_exact_system_actor': draft['owner'] == 'c0629449-1209-4b50-afac-f558a9a0fc7f',
|
||||
'first_view_selected': draft['first_view'] is True}
|
||||
normalized = copy.deepcopy(after['behavior'])
|
||||
if checks['literal_name_after']:
|
||||
normalized['draft']['name'] = expected_name
|
||||
checks['complete_behavior_equal_except_deliberate_suffix'] = normalized == before['behavior']
|
||||
for field in ('accepted', 'workspace', 'property', 'workflow', 'selected'):
|
||||
checks[field + '_exact_json_equality'] = before[field] == after[field]
|
||||
reserved = {field: draft[field] for field in ('element', 'diagram', 'placement')}
|
||||
checks['reserved_ids_unchanged'] = all(after['behavior']['draft'][k] == v for k, v in reserved.items())
|
||||
inputs = {str(p.relative_to(ROOT)): inventory(p) for p in (EXPECTED, ACTUAL)}
|
||||
expected_names = set(inputs[str(EXPECTED.relative_to(ROOT))])
|
||||
actual_names = set(inputs[str(ACTUAL.relative_to(ROOT))])
|
||||
checks['complete_file_inventory_equal'] = expected_names == actual_names
|
||||
byte_results = {name: (EXPECTED / name).read_bytes() == (ACTUAL / name).read_bytes()
|
||||
for name in expected_names if name in actual_names}
|
||||
checks['complete_file_bytes_equal'] = all(byte_results.values()) and len(byte_results) == len(expected_names)
|
||||
checks['accepted_snapshot_exactly_matches_saved_checkpoint'] = (
|
||||
set(before['accepted']['files']) == expected_names and
|
||||
all(text.encode('utf-8') == (EXPECTED / name).read_bytes()
|
||||
for name, text in before['accepted']['files'].items()))
|
||||
checks['reserved_objects_not_committed'] = all(
|
||||
all(value.encode('utf-8') not in (ACTUAL / name).read_bytes() for value in reserved.values())
|
||||
for name in actual_names)
|
||||
command = [str(EXE), 'same', str(EXPECTED / 'project.arcadia'), str(ACTUAL / 'project.arcadia')]
|
||||
env = os.environ.copy()
|
||||
env['PATH'] = str(STAGE / 'bin') + os.pathsep + env.get('PATH', '')
|
||||
started = datetime.datetime.now(datetime.timezone.utc).isoformat()
|
||||
tick = time.perf_counter()
|
||||
run = subprocess.run(command, cwd=ROOT, env=env, capture_output=True, text=True, encoding='utf-8', errors='replace', timeout=60)
|
||||
elapsed = time.perf_counter() - tick
|
||||
checks['oracle_same_pass'] = run.returncode == 0
|
||||
checks['input_files_unchanged'] = inputs == {str(p.relative_to(ROOT)): inventory(p) for p in (EXPECTED, ACTUAL)}
|
||||
checks['evidence_and_prior_manifest_unchanged'] = artifacts == [record(ROOT / item['path']) for item in artifacts]
|
||||
result = {'command_argv': command, 'exit_code': run.returncode,
|
||||
'elapsed_seconds': round(elapsed, 6), 'stdout': run.stdout, 'stderr': run.stderr}
|
||||
(OUT / 'recovery-commands-results.log').write_text(json.dumps(result, indent=2) + '\n', encoding='utf-8', newline='\n')
|
||||
manifest = {'reviewer': 'Codex-9e8r6t /root/palette_finish', 'started_utc': started,
|
||||
'finished_utc': datetime.datetime.now(datetime.timezone.utc).isoformat(),
|
||||
'scope': 'Immutable evidence only; native observations supplied separately by root. No live work/profile/UI/Cargo access; no persisted Undo claim.',
|
||||
'artifacts': artifacts, 'input_file_inventories': inputs, 'checks': checks,
|
||||
'reserved_ids': reserved, 'reopen_recovery_cancel_direct_byte_comparisons': byte_results,
|
||||
'comparison_origin': 'checkpoint-creation-reopen', 'command': result,
|
||||
'log': record(OUT / 'recovery-commands-results.log'), 'pass': all(checks.values())}
|
||||
(OUT / 'recovery-manifest.json').write_text(json.dumps(manifest, indent=2) + '\n', encoding='utf-8', newline='\n')
|
||||
print(json.dumps({'pass': manifest['pass'], 'checks': checks, 'manifest': record(OUT / 'recovery-manifest.json')}))
|
||||
raise SystemExit(0 if manifest['pass'] else 1)
|
||||
@@ -0,0 +1,93 @@
|
||||
"""Read only frozen checkpoints; run the existing v2 oracle without compiling."""
|
||||
from pathlib import Path
|
||||
import datetime
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
OUT = Path(__file__).resolve().parent
|
||||
STAGE = ROOT / '.local/native-qa/behavior-20260911-v4'
|
||||
BASE = ROOT / '.local/native-qa/palette-20260910-v3/checkpoint-transition-reopen'
|
||||
EXE = ROOT / '.local/behavior-creation-oracle-20260911/behavior-creation-contract-v2.exe'
|
||||
|
||||
|
||||
def record(path):
|
||||
data = path.read_bytes()
|
||||
return {'path': path.relative_to(ROOT).as_posix(), 'bytes': len(data),
|
||||
'sha256': hashlib.sha256(data).hexdigest()}
|
||||
|
||||
|
||||
def inventory(directory):
|
||||
return {p.relative_to(directory).as_posix(): {
|
||||
'bytes': p.stat().st_size, 'sha256': hashlib.sha256(p.read_bytes()).hexdigest()
|
||||
} for p in sorted(directory.rglob('*')) if p.is_file()}
|
||||
|
||||
|
||||
def entry(directory):
|
||||
return str(directory / 'project.arcadia')
|
||||
|
||||
|
||||
checkpoints = {'baseline': BASE}
|
||||
for family in ('process', 'scenario', 'machine'):
|
||||
for suffix in ('', '-undo', '-redo'):
|
||||
checkpoints[family + suffix] = STAGE / ('checkpoint-' + family + suffix)
|
||||
checkpoints['creation-reopen'] = STAGE / 'checkpoint-creation-reopen'
|
||||
before = {name: inventory(path) for name, path in checkpoints.items()}
|
||||
artifacts = [record(p) for p in (
|
||||
EXE, ROOT / 'harness/behavior-creation-contract.rs', Path(__file__),
|
||||
STAGE / 'bin/arcadia-studio.exe', STAGE / 'bin/libunwind.dll',
|
||||
STAGE / 'bin/manifest.json', STAGE / 'baseline-source-manifest.json',
|
||||
STAGE / 'cancel-save-verification.json', STAGE / 'scenario-empty-saved.png',
|
||||
STAGE / 'machine-created-saved.png')]
|
||||
assert artifacts[0]['sha256'] == '056eefd444924916bf6ac3c4897ab2ff4a8a4bb892e1dbc16adad32e59bf6a36'
|
||||
assert artifacts[1]['sha256'] == '51ab2ffa1826dd949b79f3a9690042a2f9e38daf9ed91517cade3861482117ba'
|
||||
commands = [('self-check', ['self-check', entry(BASE)]),
|
||||
('baseline', ['check', 'baseline', entry(BASE), entry(BASE), entry(BASE)])]
|
||||
previous = BASE
|
||||
for family in ('process', 'scenario', 'machine'):
|
||||
first = checkpoints[family]
|
||||
commands.extend([
|
||||
(family, ['check', family, entry(BASE), entry(first), entry(first)]),
|
||||
(family + '-undo', ['same', entry(previous), entry(checkpoints[family + '-undo']), entry(first)]),
|
||||
(family + '-redo', ['check', family, entry(BASE), entry(checkpoints[family + '-redo']), entry(first)])])
|
||||
previous = first
|
||||
commands.extend([
|
||||
('reopen-contract', ['check', 'machine', entry(BASE), entry(checkpoints['creation-reopen']), entry(checkpoints['machine'])]),
|
||||
('reopen-exact-redo', ['same', entry(checkpoints['machine-redo']), entry(checkpoints['creation-reopen'])])])
|
||||
env = os.environ.copy()
|
||||
env['PATH'] = str(STAGE / 'bin') + os.pathsep + env.get('PATH', '')
|
||||
started = datetime.datetime.now(datetime.timezone.utc).isoformat()
|
||||
results = []
|
||||
for label, args in commands:
|
||||
t = time.perf_counter()
|
||||
command = [str(EXE), *args]
|
||||
run = subprocess.run(command, cwd=ROOT, env=env, capture_output=True, text=True, encoding='utf-8', errors='replace', timeout=60)
|
||||
results.append({'label': label, 'command_argv': command, 'exit_code': run.returncode,
|
||||
'elapsed_seconds': round(time.perf_counter() - t, 6),
|
||||
'stdout': run.stdout, 'stderr': run.stderr})
|
||||
after = {name: inventory(path) for name, path in checkpoints.items()}
|
||||
redo_files = checkpoints['machine-redo']
|
||||
reopen_files = checkpoints['creation-reopen']
|
||||
same_names = set(before['machine-redo']) == set(before['creation-reopen'])
|
||||
byte_results = {name: (redo_files / name).read_bytes() == (reopen_files / name).read_bytes()
|
||||
for name in before['machine-redo'] if (reopen_files / name).is_file()}
|
||||
success = (all(r['exit_code'] == 0 for r in results) and before == after
|
||||
and same_names and all(byte_results.values()))
|
||||
log = '\n\n'.join(f"[{r['label']}] argv={json.dumps(r['command_argv'])}\nexit={r['exit_code']} elapsed={r['elapsed_seconds']}s\nstdout:\n{r['stdout']}stderr:\n{r['stderr']}" for r in results)
|
||||
(OUT / 'commands-results.log').write_text(log + '\n', encoding='utf-8', newline='\n')
|
||||
manifest = {'reviewer': 'Codex-9e8r6t /root/palette_finish', 'started_utc': started,
|
||||
'finished_utc': datetime.datetime.now(datetime.timezone.utc).isoformat(),
|
||||
'scope': 'Frozen checkpoints only; no live work/profile/UI access or compilation.',
|
||||
'artifacts': artifacts, 'checkpoints': {name: {'path': path.relative_to(ROOT).as_posix(), 'files': before[name]} for name, path in checkpoints.items()},
|
||||
'commands': results, 'all_checkpoint_files_unchanged': before == after,
|
||||
'machine_redo_reopen_inventory_equal': same_names,
|
||||
'machine_redo_reopen_direct_byte_comparisons': byte_results,
|
||||
'log': record(OUT / 'commands-results.log'), 'pass': success}
|
||||
(OUT / 'manifest.json').write_text(json.dumps(manifest, indent=2) + '\n', encoding='utf-8', newline='\n')
|
||||
print(json.dumps({'pass': success, 'commands_passed': sum(r['exit_code'] == 0 for r in results),
|
||||
'commands_total': len(results), 'unchanged_checkpoints': before == after,
|
||||
'reopen_exact_files': len(byte_results), 'manifest': record(OUT / 'manifest.json')}))
|
||||
raise SystemExit(0 if success else 1)
|
||||
@@ -0,0 +1,37 @@
|
||||
# Existing source-7 constructor defaults
|
||||
|
||||
Compatibility experiment, not proposed grammar. `probe.rs` imports only the
|
||||
matching existing `oa_core` rlib; it performs no production or Cargo edits.
|
||||
|
||||
In a coordinated direct-rustc writer slot:
|
||||
|
||||
```powershell
|
||||
. .local/toolchain/env.ps1
|
||||
rustc --edition 2024 -D warnings --crate-name constructor_defaults_probe harness/experiments/constructor-defaults-20260911/probe.rs --extern oa_core=.local/target-gnullvm/debug/deps/liboa_core-3e81c616fea1d901.rlib -L dependency=.local/target-gnullvm/debug/deps -C linker=clang.exe -o .local/constructor-defaults-20260911/probe.exe
|
||||
& .local/constructor-defaults-20260911/probe.exe NEW_ISOLATED_OUTPUT_DIRECTORY
|
||||
```
|
||||
|
||||
Use a fresh output directory when reproducing so the committed evidence is not
|
||||
overwritten. The rlib SHA256 is
|
||||
`5018A1FD7286ED9DFEDCBBF5B68B399AD5D143F2A9AAAED3078CFC934C9589C4`.
|
||||
The exact compiler, core source hashes, executable hash and result inventory
|
||||
are in `provenance.json`.
|
||||
|
||||
`results/results.tsv` records **54 admitted explicit/omitted cases** and
|
||||
**12 missing-required-field refusals**, covering both OA and SA source 7.
|
||||
Every admitted occurrence is compared with a literal expected typed body and
|
||||
the full explicit-case Project. It also requires exact source retention on
|
||||
stabilization/no-op, a name-only patch retaining omissions, and canonical
|
||||
format/reparse equality. Both scenario viewpoint omission cases separately
|
||||
require MixedLegacy equality and canonical stability.
|
||||
|
||||
The `.canonical.arcadia` siblings intentionally expand hydrated defaults;
|
||||
they are not expected to retain authored omission. Exchange `operation none`
|
||||
is deliberately omitted by canonical serialization. Files named
|
||||
`omit-bindings`, `omit-kind`, `omit-operator`, `omit-branches`, `omit-guard`, or
|
||||
`omit-occurrences` are **expected invalid fixtures**, retained to show the
|
||||
specific missing-field diagnostic. They are not modeling examples.
|
||||
|
||||
The program/result files are durable evidence. The compiled executable and
|
||||
initial/final build logs remain in ignored `.local/constructor-defaults-20260911/`.
|
||||
Other constructor families and old schema versions were not exhaustively tested.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user