Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44fca0c041 | ||
|
|
fadff405b8 | ||
|
|
4994994ee0 | ||
|
|
20e3b824de | ||
|
|
fa62e90a9a | ||
|
|
9fd9107c1c | ||
|
|
234e01e27d |
+9
-3
@@ -31,11 +31,17 @@ crates/oa-desktop/src/recovery/fixtures/*.json -text
|
||||
harness/fixtures/source7-native/** -text
|
||||
harness/fixtures/basic-editing/** -text
|
||||
harness/behavior-creation-contract.rs -text whitespace=cr-at-eol
|
||||
harness/experiments/source-cli-independent-20260911/** -text whitespace=cr-at-eol
|
||||
harness/experiments/authoring-prerelease-20260911/** -text whitespace=cr-at-eol
|
||||
harness/experiments/cli-packaging-20260911/** -text whitespace=cr-at-eol
|
||||
|
||||
# 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
|
||||
|
||||
# Preserve exact inputs of the frozen D4D9 authoring milestone build.
|
||||
crates/oa-desktop/src/semantic_review/artifacts_view.rs -text
|
||||
crates/oa-desktop/src/semantic_review/attachments_view.rs -text
|
||||
crates/oa-desktop/src/semantic_review/keyed_rows.rs -text
|
||||
crates/oa-desktop/src/semantic_review/keyed_tests.rs -text
|
||||
crates/oa-desktop/src/semantic_review/keyed.rs -text
|
||||
crates/oa-desktop/src/semantic_review/ordered.rs -text
|
||||
docs/openapi.json -text
|
||||
|
||||
@@ -1,14 +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.
|
||||
- Publish versioned Gitea releases at meaningful, working milestones so the user can see progress and try the application. Include runnable builds or appropriate distributable artifacts, concise release notes, launch instructions, known limitations, and what was verified. Mark unfinished versions as prereleases. Coordinate release ownership to avoid duplicates.
|
||||
- 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,15 +2139,6 @@ 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", "crates/oa-cli"]
|
||||
members = ["crates/oa-core", "crates/oa-desktop", "crates/oa-server"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
|
||||
+32
-600
@@ -1,612 +1,45 @@
|
||||
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
|
||||
|
||||
### 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
|
||||
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.
|
||||
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.
|
||||
@@ -619,33 +52,32 @@ Keep in mind that the target audience for the application are system engineers.
|
||||
|
||||
The application should be a general systems modeling tool, like ARCADIA Capella or SysML v2
|
||||
|
||||
You should implement most of the ARCADIA semantics, while adressing original ARCADIA Capella problems / downsides
|
||||
You should implement most of the ARCADIA semantics, while adressing original ARCADIA Capella problems / downsides
|
||||
|
||||
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 (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
|
||||
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
|
||||
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 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 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 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
|
||||
@Computer
|
||||
|
||||
### Semantic model / metamodel
|
||||
|
||||
Requirements:
|
||||
|
||||
- Textual notation.
|
||||
- Graphical notation.
|
||||
- Constraints and calculations.
|
||||
@@ -658,7 +90,6 @@ Requirements:
|
||||
#### 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.
|
||||
@@ -672,8 +103,9 @@ Long, homogeneous lists in the UI that require user to scroll far or search for
|
||||
|
||||
## 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
|
||||
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
|
||||
|
||||
---
|
||||
---
|
||||
|
||||
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
|
||||
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,7 +18,7 @@ From the repository root:
|
||||
cargo run --locked -p oa-desktop --bin arcadia-studio
|
||||
```
|
||||
|
||||
Build the desktop, server and source CLI release executables:
|
||||
Build both desktop and server release executables:
|
||||
|
||||
```sh
|
||||
cargo build --release --workspace --locked
|
||||
@@ -51,21 +51,6 @@ 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.
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
[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
|
||||
@@ -1,451 +0,0 @@
|
||||
//! 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,
|
||||
})
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,425 +0,0 @@
|
||||
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 雨"
|
||||
);
|
||||
}
|
||||
@@ -1,147 +1,147 @@
|
||||
use super::artifacts::{Editor, Event, Raw};
|
||||
use super::*;
|
||||
use iced::{
|
||||
Element as View, Fill,
|
||||
widget::{button, checkbox, column, pick_list, row, text, text_editor, text_input},
|
||||
};
|
||||
pub(super) fn pairs<'a>(editor: &'a Editor, p: &'a Project, trace: &'a Trace) -> View<'a, Event> {
|
||||
let mut content=column![text("Artifact links and metadata").size(16),text("Pair links attached to these reviewed owners. Presence is attachment to the owner; taking identified source absence deletes the destination link, while a destination-only link is retained.").size(12),text_input("Filter artifact identities",&editor.search).on_input(Event::Search).padding(7)].spacing(9);
|
||||
for (layer, label, selected) in [
|
||||
(Layer::Oa, "Operational", editor.oa.clone()),
|
||||
(Layer::Sa, "System", editor.sa.clone()),
|
||||
] {
|
||||
content = content.push(text(label).size(12)).push(
|
||||
row![
|
||||
pick_list(editor.choices(p, trace, layer), selected, move |v| {
|
||||
Event::Pick(layer, Some(v))
|
||||
})
|
||||
.placeholder("Choose exact artifact identity")
|
||||
.width(Fill)
|
||||
.padding(7),
|
||||
button("No identity")
|
||||
.padding(7)
|
||||
.on_press(Event::Pick(layer, None))
|
||||
]
|
||||
.spacing(7),
|
||||
);
|
||||
}
|
||||
content.push(row![button("Reserve a new destination counterpart").padding(7).on_press(Event::Reserve),button("Review this exact artifact pair").padding(7).on_press(Event::Stage)].spacing(7).wrap()).push(text("Reservation retains a fresh identity and seeds metadata from the source. It creates nothing until Presence and all seven metadata facets are explicitly reviewed and the combined Apply succeeds. New unrelated links use ordinary artifact creation.").size(11)).padding(iced::Padding::default().right(18)).into()
|
||||
}
|
||||
pub(super) fn candidate<'a>(
|
||||
editor: &'a Editor,
|
||||
p: &'a Project,
|
||||
facet: &'a FacetSelector,
|
||||
) -> Option<View<'a, Event>> {
|
||||
let value = editor.drafts.get(facet)?;
|
||||
let (_, which) = artifacts::parts(facet)?;
|
||||
let mut content =
|
||||
column![text(format!("Final artifact {}", artifacts::field(which)?)).size(12)].spacing(7);
|
||||
match value {
|
||||
Raw::Text(_) if which == ArtifactFacet::Description => {
|
||||
let draft = editor.descriptions.get(facet)?;
|
||||
let facet = facet.clone();
|
||||
content = content.push(
|
||||
text_editor(draft)
|
||||
.height(110)
|
||||
.on_action(move |action| Event::Description(facet.clone(), action)),
|
||||
);
|
||||
}
|
||||
Raw::Text(value) => {
|
||||
let facet = facet.clone();
|
||||
content = content.push(
|
||||
text_input("Exact text", value)
|
||||
.on_input(move |value| Event::Input(facet.clone(), Raw::Text(value)))
|
||||
.padding(7),
|
||||
);
|
||||
}
|
||||
Raw::Owner(value) => {
|
||||
let mut choices: Vec<_> = p
|
||||
.identities()
|
||||
.into_iter()
|
||||
.map(|(id, alias)| Choice {
|
||||
id,
|
||||
label: format!("{} · @{alias}", p.named(id)),
|
||||
})
|
||||
.collect();
|
||||
choices.sort_by_cached_key(|c| c.label.to_lowercase());
|
||||
let selected = value.parse::<Id>().ok().map(|id| view::choice(p, id));
|
||||
let raw_facet = facet.clone();
|
||||
let pick_facet = facet.clone();
|
||||
content = content
|
||||
.push(
|
||||
pick_list(choices, selected, move |v: Choice| {
|
||||
Event::Input(pick_facet.clone(), Raw::Owner(v.id.to_string()))
|
||||
})
|
||||
.placeholder("Explicit final owner")
|
||||
.width(Fill)
|
||||
.padding(7),
|
||||
)
|
||||
.push(
|
||||
text_input("Owner identity (retains rejected input)", value)
|
||||
.on_input(move |v| Event::Input(raw_facet.clone(), Raw::Owner(v)))
|
||||
.padding(7),
|
||||
);
|
||||
}
|
||||
Raw::Relation(value) => {
|
||||
let facet = facet.clone();
|
||||
let variants = [
|
||||
ArtifactRelation::References,
|
||||
ArtifactRelation::Satisfies,
|
||||
ArtifactRelation::Verifies,
|
||||
ArtifactRelation::Implements,
|
||||
ArtifactRelation::DerivedFrom,
|
||||
];
|
||||
let choices: Vec<_> = variants.into_iter().map(RelationChoice).collect();
|
||||
content = content.push(
|
||||
pick_list(choices, Some(RelationChoice(*value)), move |v| {
|
||||
Event::Input(facet.clone(), Raw::Relation(v.0))
|
||||
})
|
||||
.padding(7),
|
||||
);
|
||||
}
|
||||
Raw::Optional {
|
||||
present,
|
||||
text: value,
|
||||
} => {
|
||||
let toggle_facet = facet.clone();
|
||||
let retained = value.clone();
|
||||
let input_facet = facet.clone();
|
||||
let enabled = *present;
|
||||
content = content
|
||||
.push(
|
||||
checkbox(*present)
|
||||
.label("Present (checked with empty text means an explicit empty value)")
|
||||
.on_toggle(move |present| {
|
||||
Event::Input(
|
||||
toggle_facet.clone(),
|
||||
Raw::Optional {
|
||||
present,
|
||||
text: retained.clone(),
|
||||
},
|
||||
)
|
||||
}),
|
||||
)
|
||||
.push(
|
||||
text_input("Retained optional text", value)
|
||||
.on_input(move |text| {
|
||||
Event::Input(
|
||||
input_facet.clone(),
|
||||
Raw::Optional {
|
||||
present: enabled,
|
||||
text,
|
||||
},
|
||||
)
|
||||
})
|
||||
.padding(7),
|
||||
);
|
||||
}
|
||||
}
|
||||
Some(content.into())
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
struct RelationChoice(ArtifactRelation);
|
||||
impl std::fmt::Display for RelationChoice {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self.0)
|
||||
}
|
||||
}
|
||||
use super::artifacts::{Editor, Event, Raw};
|
||||
use super::*;
|
||||
use iced::{
|
||||
Element as View, Fill,
|
||||
widget::{button, checkbox, column, pick_list, row, text, text_editor, text_input},
|
||||
};
|
||||
pub(super) fn pairs<'a>(editor: &'a Editor, p: &'a Project, trace: &'a Trace) -> View<'a, Event> {
|
||||
let mut content=column![text("Artifact links and metadata").size(16),text("Pair links attached to these reviewed owners. Presence is attachment to the owner; taking identified source absence deletes the destination link, while a destination-only link is retained.").size(12),text_input("Filter artifact identities",&editor.search).on_input(Event::Search).padding(7)].spacing(9);
|
||||
for (layer, label, selected) in [
|
||||
(Layer::Oa, "Operational", editor.oa.clone()),
|
||||
(Layer::Sa, "System", editor.sa.clone()),
|
||||
] {
|
||||
content = content.push(text(label).size(12)).push(
|
||||
row![
|
||||
pick_list(editor.choices(p, trace, layer), selected, move |v| {
|
||||
Event::Pick(layer, Some(v))
|
||||
})
|
||||
.placeholder("Choose exact artifact identity")
|
||||
.width(Fill)
|
||||
.padding(7),
|
||||
button("No identity")
|
||||
.padding(7)
|
||||
.on_press(Event::Pick(layer, None))
|
||||
]
|
||||
.spacing(7),
|
||||
);
|
||||
}
|
||||
content.push(row![button("Reserve a new destination counterpart").padding(7).on_press(Event::Reserve),button("Review this exact artifact pair").padding(7).on_press(Event::Stage)].spacing(7).wrap()).push(text("Reservation retains a fresh identity and seeds metadata from the source. It creates nothing until Presence and all seven metadata facets are explicitly reviewed and the combined Apply succeeds. New unrelated links use ordinary artifact creation.").size(11)).padding(iced::Padding::default().right(18)).into()
|
||||
}
|
||||
pub(super) fn candidate<'a>(
|
||||
editor: &'a Editor,
|
||||
p: &'a Project,
|
||||
facet: &'a FacetSelector,
|
||||
) -> Option<View<'a, Event>> {
|
||||
let value = editor.drafts.get(facet)?;
|
||||
let (_, which) = artifacts::parts(facet)?;
|
||||
let mut content =
|
||||
column![text(format!("Final artifact {}", artifacts::field(which)?)).size(12)].spacing(7);
|
||||
match value {
|
||||
Raw::Text(_) if which == ArtifactFacet::Description => {
|
||||
let draft = editor.descriptions.get(facet)?;
|
||||
let facet = facet.clone();
|
||||
content = content.push(
|
||||
text_editor(draft)
|
||||
.height(110)
|
||||
.on_action(move |action| Event::Description(facet.clone(), action)),
|
||||
);
|
||||
}
|
||||
Raw::Text(value) => {
|
||||
let facet = facet.clone();
|
||||
content = content.push(
|
||||
text_input("Exact text", value)
|
||||
.on_input(move |value| Event::Input(facet.clone(), Raw::Text(value)))
|
||||
.padding(7),
|
||||
);
|
||||
}
|
||||
Raw::Owner(value) => {
|
||||
let mut choices: Vec<_> = p
|
||||
.identities()
|
||||
.into_iter()
|
||||
.map(|(id, alias)| Choice {
|
||||
id,
|
||||
label: format!("{} · @{alias}", p.named(id)),
|
||||
})
|
||||
.collect();
|
||||
choices.sort_by_cached_key(|c| c.label.to_lowercase());
|
||||
let selected = value.parse::<Id>().ok().map(|id| view::choice(p, id));
|
||||
let raw_facet = facet.clone();
|
||||
let pick_facet = facet.clone();
|
||||
content = content
|
||||
.push(
|
||||
pick_list(choices, selected, move |v: Choice| {
|
||||
Event::Input(pick_facet.clone(), Raw::Owner(v.id.to_string()))
|
||||
})
|
||||
.placeholder("Explicit final owner")
|
||||
.width(Fill)
|
||||
.padding(7),
|
||||
)
|
||||
.push(
|
||||
text_input("Owner identity (retains rejected input)", value)
|
||||
.on_input(move |v| Event::Input(raw_facet.clone(), Raw::Owner(v)))
|
||||
.padding(7),
|
||||
);
|
||||
}
|
||||
Raw::Relation(value) => {
|
||||
let facet = facet.clone();
|
||||
let variants = [
|
||||
ArtifactRelation::References,
|
||||
ArtifactRelation::Satisfies,
|
||||
ArtifactRelation::Verifies,
|
||||
ArtifactRelation::Implements,
|
||||
ArtifactRelation::DerivedFrom,
|
||||
];
|
||||
let choices: Vec<_> = variants.into_iter().map(RelationChoice).collect();
|
||||
content = content.push(
|
||||
pick_list(choices, Some(RelationChoice(*value)), move |v| {
|
||||
Event::Input(facet.clone(), Raw::Relation(v.0))
|
||||
})
|
||||
.padding(7),
|
||||
);
|
||||
}
|
||||
Raw::Optional {
|
||||
present,
|
||||
text: value,
|
||||
} => {
|
||||
let toggle_facet = facet.clone();
|
||||
let retained = value.clone();
|
||||
let input_facet = facet.clone();
|
||||
let enabled = *present;
|
||||
content = content
|
||||
.push(
|
||||
checkbox(*present)
|
||||
.label("Present (checked with empty text means an explicit empty value)")
|
||||
.on_toggle(move |present| {
|
||||
Event::Input(
|
||||
toggle_facet.clone(),
|
||||
Raw::Optional {
|
||||
present,
|
||||
text: retained.clone(),
|
||||
},
|
||||
)
|
||||
}),
|
||||
)
|
||||
.push(
|
||||
text_input("Retained optional text", value)
|
||||
.on_input(move |text| {
|
||||
Event::Input(
|
||||
input_facet.clone(),
|
||||
Raw::Optional {
|
||||
present: enabled,
|
||||
text,
|
||||
},
|
||||
)
|
||||
})
|
||||
.padding(7),
|
||||
);
|
||||
}
|
||||
}
|
||||
Some(content.into())
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
struct RelationChoice(ArtifactRelation);
|
||||
impl std::fmt::Display for RelationChoice {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,79 +1,79 @@
|
||||
//! Explicit attachment pair and final destination field controls.
|
||||
use super::{
|
||||
attachments::{Editor, Event, Family},
|
||||
*,
|
||||
};
|
||||
use iced::{
|
||||
Element as View, Fill,
|
||||
widget::{button, column, pick_list, row, text, text_input},
|
||||
};
|
||||
|
||||
pub(super) fn pairs<'a>(
|
||||
editor: &'a Editor,
|
||||
project: &'a Project,
|
||||
trace: &'a Trace,
|
||||
) -> View<'a, Event> {
|
||||
let mut content = column![
|
||||
text("Existing children and relationship endpoints").size(16),
|
||||
text("Choose explicit Operational and System identities. A child's final parent or a relationship's final endpoint is reviewed independently; opening another trace keeps this draft.").size(12),
|
||||
pick_list([Family::Child, Family::Source, Family::Target], Some(editor.family), Event::Family).text_size(12).padding(7).width(Fill),
|
||||
text_input("Filter attachment identities by name or alias", &editor.search).on_input(Event::Search).size(12).padding(7),
|
||||
].spacing(9);
|
||||
for layer in [Layer::Oa, Layer::Sa] {
|
||||
let label = if layer == Layer::Oa {
|
||||
"Operational"
|
||||
} else {
|
||||
"System"
|
||||
};
|
||||
let owner = if layer == Layer::Oa {
|
||||
trace.oa
|
||||
} else {
|
||||
trace.sa
|
||||
};
|
||||
content = content.push(text(format!("{label} owner: {}", project.named(owner))).size(12));
|
||||
content = content.push(
|
||||
row![
|
||||
pick_list(
|
||||
editor.choices(project, trace, layer),
|
||||
editor.selection(layer).cloned(),
|
||||
move |choice| Event::Pick(layer, Some(choice))
|
||||
)
|
||||
.placeholder(format!("Choose an existing {label} identity"))
|
||||
.text_size(12)
|
||||
.padding(7)
|
||||
.width(Fill),
|
||||
button(text("No identity on this side").size(11))
|
||||
.style(crate::style::nav)
|
||||
.padding(7)
|
||||
.on_press(Event::Pick(layer, None)),
|
||||
]
|
||||
.spacing(7),
|
||||
);
|
||||
}
|
||||
content = content.push(
|
||||
button(text("Use exact attachment pair").size(12))
|
||||
.padding(8)
|
||||
.style(crate::style::nav)
|
||||
.on_press(Event::Stage),
|
||||
);
|
||||
content.push(text("Taking an absent relationship endpoint requires an explicit final replacement. New child and relationship creation uses a separate explicit transition mapping.").size(11)).padding(iced::Padding::default().right(18)).into()
|
||||
}
|
||||
|
||||
pub(super) fn candidate<'a>(
|
||||
editor: &'a Editor,
|
||||
project: &'a Project,
|
||||
facet: &'a FacetSelector,
|
||||
layer: Layer,
|
||||
) -> Option<View<'a, Message>> {
|
||||
let draft = editor.drafts.get(facet)?;
|
||||
let (id, _) = attachments::actual_slot(facet, layer)?;
|
||||
let path = attachments::path(project, facet, layer)?;
|
||||
Some(
|
||||
column![
|
||||
text(format!("Destination record: {}", project.named(id))).size(12),
|
||||
crate::forms::scoped(draft, project, &path, attachments::label(facet)),
|
||||
]
|
||||
.spacing(7)
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
//! Explicit attachment pair and final destination field controls.
|
||||
use super::{
|
||||
attachments::{Editor, Event, Family},
|
||||
*,
|
||||
};
|
||||
use iced::{
|
||||
Element as View, Fill,
|
||||
widget::{button, column, pick_list, row, text, text_input},
|
||||
};
|
||||
|
||||
pub(super) fn pairs<'a>(
|
||||
editor: &'a Editor,
|
||||
project: &'a Project,
|
||||
trace: &'a Trace,
|
||||
) -> View<'a, Event> {
|
||||
let mut content = column![
|
||||
text("Existing children and relationship endpoints").size(16),
|
||||
text("Choose explicit Operational and System identities. A child's final parent or a relationship's final endpoint is reviewed independently; opening another trace keeps this draft.").size(12),
|
||||
pick_list([Family::Child, Family::Source, Family::Target], Some(editor.family), Event::Family).text_size(12).padding(7).width(Fill),
|
||||
text_input("Filter attachment identities by name or alias", &editor.search).on_input(Event::Search).size(12).padding(7),
|
||||
].spacing(9);
|
||||
for layer in [Layer::Oa, Layer::Sa] {
|
||||
let label = if layer == Layer::Oa {
|
||||
"Operational"
|
||||
} else {
|
||||
"System"
|
||||
};
|
||||
let owner = if layer == Layer::Oa {
|
||||
trace.oa
|
||||
} else {
|
||||
trace.sa
|
||||
};
|
||||
content = content.push(text(format!("{label} owner: {}", project.named(owner))).size(12));
|
||||
content = content.push(
|
||||
row![
|
||||
pick_list(
|
||||
editor.choices(project, trace, layer),
|
||||
editor.selection(layer).cloned(),
|
||||
move |choice| Event::Pick(layer, Some(choice))
|
||||
)
|
||||
.placeholder(format!("Choose an existing {label} identity"))
|
||||
.text_size(12)
|
||||
.padding(7)
|
||||
.width(Fill),
|
||||
button(text("No identity on this side").size(11))
|
||||
.style(crate::style::nav)
|
||||
.padding(7)
|
||||
.on_press(Event::Pick(layer, None)),
|
||||
]
|
||||
.spacing(7),
|
||||
);
|
||||
}
|
||||
content = content.push(
|
||||
button(text("Use exact attachment pair").size(12))
|
||||
.padding(8)
|
||||
.style(crate::style::nav)
|
||||
.on_press(Event::Stage),
|
||||
);
|
||||
content.push(text("Taking an absent relationship endpoint requires an explicit final replacement. New child and relationship creation uses a separate explicit transition mapping.").size(11)).padding(iced::Padding::default().right(18)).into()
|
||||
}
|
||||
|
||||
pub(super) fn candidate<'a>(
|
||||
editor: &'a Editor,
|
||||
project: &'a Project,
|
||||
facet: &'a FacetSelector,
|
||||
layer: Layer,
|
||||
) -> Option<View<'a, Message>> {
|
||||
let draft = editor.drafts.get(facet)?;
|
||||
let (id, _) = attachments::actual_slot(facet, layer)?;
|
||||
let path = attachments::path(project, facet, layer)?;
|
||||
Some(
|
||||
column![
|
||||
text(format!("Destination record: {}", project.named(id))).size(12),
|
||||
crate::forms::scoped(draft, project, &path, attachments::label(facet)),
|
||||
]
|
||||
.spacing(7)
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,350 +1,350 @@
|
||||
//! Closed presence-only adapter. The core independently validates the full final record.
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum Family {
|
||||
Member(ReferenceCollection),
|
||||
Literal,
|
||||
Annotation,
|
||||
Binding,
|
||||
}
|
||||
impl std::fmt::Display for Family {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
Self::Member(ReferenceCollection::FunctionInputItems) => "Function input items",
|
||||
Self::Member(ReferenceCollection::FunctionOutputItems) => "Function output items",
|
||||
Self::Member(ReferenceCollection::SignalItems) => "Signal items",
|
||||
Self::Member(ReferenceCollection::CategoryExchanges) => "Category exchanges",
|
||||
Self::Member(ReferenceCollection::ExchangeItems) => "Exchange items",
|
||||
Self::Member(ReferenceCollection::TransitionTriggers) => "Transition triggers",
|
||||
Self::Member(ReferenceCollection::TransitionEffects) => "Transition effects",
|
||||
Self::Member(ReferenceCollection::Constrained) => "Constrained identities",
|
||||
Self::Member(ReferenceCollection::Steps) => "Ordered steps",
|
||||
Self::Member(ReferenceCollection::FragmentBranches) => "Fragment branches",
|
||||
Self::Literal => "Enumeration literals",
|
||||
Self::Annotation => "Message values",
|
||||
Self::Binding => "Scenario lifeline bindings",
|
||||
})
|
||||
}
|
||||
}
|
||||
pub(super) const FAMILIES: [Family; 13] = [
|
||||
Family::Member(ReferenceCollection::Steps),
|
||||
Family::Member(ReferenceCollection::FragmentBranches),
|
||||
Family::Member(ReferenceCollection::FunctionInputItems),
|
||||
Family::Member(ReferenceCollection::FunctionOutputItems),
|
||||
Family::Member(ReferenceCollection::SignalItems),
|
||||
Family::Member(ReferenceCollection::ExchangeItems),
|
||||
Family::Member(ReferenceCollection::CategoryExchanges),
|
||||
Family::Member(ReferenceCollection::TransitionTriggers),
|
||||
Family::Member(ReferenceCollection::TransitionEffects),
|
||||
Family::Member(ReferenceCollection::Constrained),
|
||||
Family::Literal,
|
||||
Family::Annotation,
|
||||
Family::Binding,
|
||||
];
|
||||
pub(super) fn family(facet: &FacetSelector) -> Option<Family> {
|
||||
let family = match facet {
|
||||
FacetSelector::Member { collection, .. } => Family::Member(*collection),
|
||||
FacetSelector::Literal(_) => Family::Literal,
|
||||
FacetSelector::Annotation(_) => Family::Annotation,
|
||||
FacetSelector::Binding(_) => Family::Binding,
|
||||
_ => return None,
|
||||
};
|
||||
FAMILIES.contains(&family).then_some(family)
|
||||
}
|
||||
pub(super) fn supported(facet: &FacetSelector) -> bool {
|
||||
family(facet).is_some()
|
||||
}
|
||||
|
||||
// Fixed paths for the closed collections and the exact enum literal set.
|
||||
// They are selected by the typed facet; callers cannot supply arbitrary paths.
|
||||
pub(super) fn path(value: &Value, family: Family) -> Option<String> {
|
||||
let prefixes = ["/body/Oa/Shared", "/body/Sa/Shared"];
|
||||
let tails: &[&str] = match family {
|
||||
Family::Member(ReferenceCollection::Steps) => {
|
||||
return [
|
||||
"/body/Oa/Process/steps",
|
||||
"/body/Sa/FunctionalChain/steps",
|
||||
"/body/Oa/Shared/Scenario/steps",
|
||||
"/body/Sa/Shared/Scenario/steps",
|
||||
"/body/Oa/Shared/Occurrence/body/Branch/occurrences",
|
||||
"/body/Sa/Shared/Occurrence/body/Branch/occurrences",
|
||||
]
|
||||
.into_iter()
|
||||
.find(|p| value.pointer(p).is_some_and(Value::is_array))
|
||||
.map(str::to_owned);
|
||||
}
|
||||
Family::Member(ReferenceCollection::FragmentBranches) => {
|
||||
&["Occurrence/body/Fragment/branches"]
|
||||
}
|
||||
Family::Member(ReferenceCollection::FunctionInputItems) => {
|
||||
&["Port/contract/FunctionInput/incoming_items"]
|
||||
}
|
||||
Family::Member(ReferenceCollection::FunctionOutputItems) => {
|
||||
&["Port/contract/FunctionOutput/outgoing_items"]
|
||||
}
|
||||
Family::Member(ReferenceCollection::CategoryExchanges) => &["ExchangeCategory/exchanges"],
|
||||
Family::Member(ReferenceCollection::SignalItems) => &["Event/body/Signal/items"],
|
||||
Family::Member(ReferenceCollection::Constrained) => &["Constraint/constrained"],
|
||||
Family::Literal => &["DataType/definition/Enumeration/literals"],
|
||||
Family::Annotation => &["Occurrence/body/Exchange/values"],
|
||||
Family::Binding => &["Occurrence/body/ScenarioUse/bindings"],
|
||||
Family::Member(ReferenceCollection::ExchangeItems) => {
|
||||
return [
|
||||
"Interaction",
|
||||
"FunctionalExchange",
|
||||
"BoundaryExchange",
|
||||
"CommunicationMean",
|
||||
]
|
||||
.iter()
|
||||
.map(|kind| format!("/body/{kind}/items"))
|
||||
.find(|p| value.pointer(p).is_some_and(Value::is_array));
|
||||
}
|
||||
Family::Member(ReferenceCollection::TransitionTriggers) => {
|
||||
return value
|
||||
.pointer("/body/StateTransition/triggers")
|
||||
.is_some_and(Value::is_array)
|
||||
.then(|| "/body/StateTransition/triggers".into());
|
||||
}
|
||||
Family::Member(ReferenceCollection::TransitionEffects) => {
|
||||
return value
|
||||
.pointer("/body/StateTransition/effects")
|
||||
.is_some_and(Value::is_array)
|
||||
.then(|| "/body/StateTransition/effects".into());
|
||||
}
|
||||
};
|
||||
prefixes
|
||||
.iter()
|
||||
.flat_map(|prefix| tails.iter().map(move |tail| format!("{prefix}/{tail}")))
|
||||
.find(|p| value.pointer(p).is_some_and(Value::is_array))
|
||||
}
|
||||
pub(super) fn key(facet: &FacetSelector, layer: Layer) -> Option<Value> {
|
||||
match facet {
|
||||
FacetSelector::Member { keys, .. } => if layer == Layer::Oa { keys.oa } else { keys.sa }
|
||||
.map(|id| serde_json::to_value(id).expect("Id")),
|
||||
FacetSelector::Literal(keys) => if layer == Layer::Oa {
|
||||
keys.oa.as_ref()
|
||||
} else {
|
||||
keys.sa.as_ref()
|
||||
}
|
||||
.map(|v| Value::String(v.clone())),
|
||||
FacetSelector::Annotation(keys) => if layer == Layer::Oa {
|
||||
keys.oa.as_ref()
|
||||
} else {
|
||||
keys.sa.as_ref()
|
||||
}
|
||||
.map(|key| serde_json::to_value(key).expect("Annotation key")),
|
||||
FacetSelector::Binding(keys) => if layer == Layer::Oa { keys.oa } else { keys.sa }
|
||||
.map(|id| serde_json::to_value(id).expect("Binding key")),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub(super) fn overlap(a: &FacetSelector, b: &FacetSelector) -> bool {
|
||||
if family(a).is_none() || family(a) != family(b) {
|
||||
return false;
|
||||
}
|
||||
[Layer::Oa, Layer::Sa]
|
||||
.into_iter()
|
||||
.any(|layer| key(a, layer).is_some() && key(a, layer) == key(b, layer))
|
||||
}
|
||||
pub(super) fn project_presence(
|
||||
value: &mut Value,
|
||||
facet: &FacetSelector,
|
||||
layer: Layer,
|
||||
present: bool,
|
||||
) -> Result<(), String> {
|
||||
let family = family(facet).ok_or("This keyed family remains read-only.")?;
|
||||
let key = key(facet, layer)
|
||||
.ok_or("Choose the destination identity in Members before setting or taking presence.")?;
|
||||
let path =
|
||||
path(value, family).ok_or("This collection is not applicable to the destination kind.")?;
|
||||
let list = value
|
||||
.pointer_mut(&path)
|
||||
.and_then(Value::as_array_mut)
|
||||
.ok_or("Destination collection is unavailable.")?;
|
||||
if present {
|
||||
if !list.contains(&key) {
|
||||
list.push(key);
|
||||
}
|
||||
} else {
|
||||
list.retain(|v| v != &key);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl ReviewRow {
|
||||
pub(super) fn keyed_value(
|
||||
&self,
|
||||
project: &Project,
|
||||
trace: &Trace,
|
||||
value: &mut Value,
|
||||
facet: &FacetSelector,
|
||||
decision: SemanticDecision,
|
||||
) -> Result<(), String> {
|
||||
if keyed_rows::supported(facet) {
|
||||
return self.project_row(project, trace, value, facet, decision);
|
||||
}
|
||||
let present = match decision {
|
||||
SemanticDecision::KeepTarget => return Ok(()),
|
||||
SemanticDecision::TakeSource => {
|
||||
// A missing source identity is a target-local row, not a removal.
|
||||
if key(facet, self.direction.source_layer()).is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
let fact = capture_facet(project, trace.oa, trace.sa, facet)?;
|
||||
scalar::value(&fact, self.direction.source_layer()).and_then(|v| v.as_bool()).ok_or("Source collection is not applicable; choose an explicit final presence or Keep.")?
|
||||
}
|
||||
SemanticDecision::Candidate => *self
|
||||
.presence
|
||||
.get(facet)
|
||||
.ok_or("Choose final membership presence.")?,
|
||||
};
|
||||
project_presence(value, facet, self.direction.target_layer(), present)
|
||||
}
|
||||
|
||||
pub(super) fn member_families(&self, project: &Project) -> Vec<Family> {
|
||||
let Some(trace) = self.effective_trace(project) else {
|
||||
return vec![];
|
||||
};
|
||||
let values: Vec<_> = [trace.oa, trace.sa]
|
||||
.into_iter()
|
||||
.filter_map(|id| Draft::from_project(project, id).map(|d| d.value))
|
||||
.collect();
|
||||
FAMILIES
|
||||
.into_iter()
|
||||
.filter(|family| values.iter().any(|value| path(value, *family).is_some()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(super) fn member_choices(&self, project: &Project, layer: Layer) -> Vec<Choice> {
|
||||
if self.member_family == Family::Binding {
|
||||
return self.binding_choices(project, layer, true);
|
||||
}
|
||||
let Some(trace) = self.effective_trace(project) else {
|
||||
return vec![];
|
||||
};
|
||||
let id = if layer == Layer::Oa {
|
||||
trace.oa
|
||||
} else {
|
||||
trace.sa
|
||||
};
|
||||
let Some(mut draft) = Draft::from_project(project, id) else {
|
||||
return vec![];
|
||||
};
|
||||
if layer == self.direction.target_layer() {
|
||||
draft.set_context(self.combined_value(project, true).ok());
|
||||
}
|
||||
let selected = if layer == Layer::Oa {
|
||||
&self.member_oa
|
||||
} else {
|
||||
&self.member_sa
|
||||
};
|
||||
let Some(path) = path(&draft.context_value(), self.member_family) else {
|
||||
return selected.iter().cloned().collect();
|
||||
};
|
||||
crate::forms::reference_choices(
|
||||
&draft,
|
||||
project,
|
||||
&format!("{path}/0"),
|
||||
selected.as_ref().map_or(Id::nil(), |c| c.id),
|
||||
)
|
||||
.into_iter()
|
||||
.filter(|c| !c.id.is_nil())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(super) fn edit_pair(
|
||||
&mut self,
|
||||
project: &Project,
|
||||
facet: FacetSelector,
|
||||
) -> Result<(), String> {
|
||||
self.member_family = family(&facet).ok_or("This keyed family remains read-only.")?;
|
||||
match facet {
|
||||
FacetSelector::Annotation(keys) => {
|
||||
self.annotation_oa.selected = keys.oa.map(|key| crate::data_values::FieldPathKey {
|
||||
path: key.path,
|
||||
field: key.field,
|
||||
});
|
||||
self.annotation_sa.selected = keys.sa.map(|key| crate::data_values::FieldPathKey {
|
||||
path: key.path,
|
||||
field: key.field,
|
||||
});
|
||||
}
|
||||
FacetSelector::Binding(keys) => {
|
||||
self.member_oa = keys.oa.map(|id| view::choice(project, id));
|
||||
self.member_sa = keys.sa.map(|id| view::choice(project, id));
|
||||
}
|
||||
FacetSelector::Member { keys, .. } => {
|
||||
self.member_oa = keys.oa.map(|id| view::choice(project, id));
|
||||
self.member_sa = keys.sa.map(|id| view::choice(project, id));
|
||||
}
|
||||
FacetSelector::Literal(keys) => {
|
||||
self.literal_oa = keys.oa.is_some();
|
||||
self.literal_sa = keys.sa.is_some();
|
||||
self.literal = keys.oa.or(keys.sa).unwrap_or_default();
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
self.tab = Tab::Members;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn stage_member(&mut self, project: &Project) -> Result<(), String> {
|
||||
if !FAMILIES.contains(&self.member_family) {
|
||||
return Err("This keyed family remains read-only.".into());
|
||||
}
|
||||
let facet = match self.member_family {
|
||||
Family::Member(collection) => FacetSelector::Member {
|
||||
collection,
|
||||
keys: KeyPair {
|
||||
oa: self.member_oa.as_ref().map(|c| c.id),
|
||||
sa: self.member_sa.as_ref().map(|c| c.id),
|
||||
},
|
||||
},
|
||||
Family::Literal => FacetSelector::Literal(KeyPair {
|
||||
oa: self.literal_oa.then(|| self.literal.clone()),
|
||||
sa: self.literal_sa.then(|| self.literal.clone()),
|
||||
}),
|
||||
Family::Annotation => FacetSelector::Annotation(KeyPair {
|
||||
oa: self
|
||||
.annotation_oa
|
||||
.selected
|
||||
.as_ref()
|
||||
.map(|key| AnnotationKey {
|
||||
path: key.path.clone(),
|
||||
field: key.field,
|
||||
}),
|
||||
sa: self
|
||||
.annotation_sa
|
||||
.selected
|
||||
.as_ref()
|
||||
.map(|key| AnnotationKey {
|
||||
path: key.path.clone(),
|
||||
field: key.field,
|
||||
}),
|
||||
}),
|
||||
Family::Binding => FacetSelector::Binding(KeyPair {
|
||||
oa: self.member_oa.as_ref().map(|c| c.id),
|
||||
sa: self.member_sa.as_ref().map(|c| c.id),
|
||||
}),
|
||||
};
|
||||
let trace = self.effective_trace(project).ok_or("Choose a trace.")?;
|
||||
capture_facet(project, trace.oa, trace.sa, &facet)?;
|
||||
self.stage_row_references(&facet)?;
|
||||
if let FacetSelector::Member {
|
||||
keys:
|
||||
KeyPair {
|
||||
oa: Some(oa),
|
||||
sa: Some(sa),
|
||||
},
|
||||
..
|
||||
} = &facet
|
||||
{
|
||||
self.references.insert(*oa, *sa);
|
||||
}
|
||||
self.staged.insert(facet.clone());
|
||||
self.selected = Some(facet);
|
||||
self.tab = Tab::Facts;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
//! Closed presence-only adapter. The core independently validates the full final record.
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum Family {
|
||||
Member(ReferenceCollection),
|
||||
Literal,
|
||||
Annotation,
|
||||
Binding,
|
||||
}
|
||||
impl std::fmt::Display for Family {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
Self::Member(ReferenceCollection::FunctionInputItems) => "Function input items",
|
||||
Self::Member(ReferenceCollection::FunctionOutputItems) => "Function output items",
|
||||
Self::Member(ReferenceCollection::SignalItems) => "Signal items",
|
||||
Self::Member(ReferenceCollection::CategoryExchanges) => "Category exchanges",
|
||||
Self::Member(ReferenceCollection::ExchangeItems) => "Exchange items",
|
||||
Self::Member(ReferenceCollection::TransitionTriggers) => "Transition triggers",
|
||||
Self::Member(ReferenceCollection::TransitionEffects) => "Transition effects",
|
||||
Self::Member(ReferenceCollection::Constrained) => "Constrained identities",
|
||||
Self::Member(ReferenceCollection::Steps) => "Ordered steps",
|
||||
Self::Member(ReferenceCollection::FragmentBranches) => "Fragment branches",
|
||||
Self::Literal => "Enumeration literals",
|
||||
Self::Annotation => "Message values",
|
||||
Self::Binding => "Scenario lifeline bindings",
|
||||
})
|
||||
}
|
||||
}
|
||||
pub(super) const FAMILIES: [Family; 13] = [
|
||||
Family::Member(ReferenceCollection::Steps),
|
||||
Family::Member(ReferenceCollection::FragmentBranches),
|
||||
Family::Member(ReferenceCollection::FunctionInputItems),
|
||||
Family::Member(ReferenceCollection::FunctionOutputItems),
|
||||
Family::Member(ReferenceCollection::SignalItems),
|
||||
Family::Member(ReferenceCollection::ExchangeItems),
|
||||
Family::Member(ReferenceCollection::CategoryExchanges),
|
||||
Family::Member(ReferenceCollection::TransitionTriggers),
|
||||
Family::Member(ReferenceCollection::TransitionEffects),
|
||||
Family::Member(ReferenceCollection::Constrained),
|
||||
Family::Literal,
|
||||
Family::Annotation,
|
||||
Family::Binding,
|
||||
];
|
||||
pub(super) fn family(facet: &FacetSelector) -> Option<Family> {
|
||||
let family = match facet {
|
||||
FacetSelector::Member { collection, .. } => Family::Member(*collection),
|
||||
FacetSelector::Literal(_) => Family::Literal,
|
||||
FacetSelector::Annotation(_) => Family::Annotation,
|
||||
FacetSelector::Binding(_) => Family::Binding,
|
||||
_ => return None,
|
||||
};
|
||||
FAMILIES.contains(&family).then_some(family)
|
||||
}
|
||||
pub(super) fn supported(facet: &FacetSelector) -> bool {
|
||||
family(facet).is_some()
|
||||
}
|
||||
|
||||
// Fixed paths for the closed collections and the exact enum literal set.
|
||||
// They are selected by the typed facet; callers cannot supply arbitrary paths.
|
||||
pub(super) fn path(value: &Value, family: Family) -> Option<String> {
|
||||
let prefixes = ["/body/Oa/Shared", "/body/Sa/Shared"];
|
||||
let tails: &[&str] = match family {
|
||||
Family::Member(ReferenceCollection::Steps) => {
|
||||
return [
|
||||
"/body/Oa/Process/steps",
|
||||
"/body/Sa/FunctionalChain/steps",
|
||||
"/body/Oa/Shared/Scenario/steps",
|
||||
"/body/Sa/Shared/Scenario/steps",
|
||||
"/body/Oa/Shared/Occurrence/body/Branch/occurrences",
|
||||
"/body/Sa/Shared/Occurrence/body/Branch/occurrences",
|
||||
]
|
||||
.into_iter()
|
||||
.find(|p| value.pointer(p).is_some_and(Value::is_array))
|
||||
.map(str::to_owned);
|
||||
}
|
||||
Family::Member(ReferenceCollection::FragmentBranches) => {
|
||||
&["Occurrence/body/Fragment/branches"]
|
||||
}
|
||||
Family::Member(ReferenceCollection::FunctionInputItems) => {
|
||||
&["Port/contract/FunctionInput/incoming_items"]
|
||||
}
|
||||
Family::Member(ReferenceCollection::FunctionOutputItems) => {
|
||||
&["Port/contract/FunctionOutput/outgoing_items"]
|
||||
}
|
||||
Family::Member(ReferenceCollection::CategoryExchanges) => &["ExchangeCategory/exchanges"],
|
||||
Family::Member(ReferenceCollection::SignalItems) => &["Event/body/Signal/items"],
|
||||
Family::Member(ReferenceCollection::Constrained) => &["Constraint/constrained"],
|
||||
Family::Literal => &["DataType/definition/Enumeration/literals"],
|
||||
Family::Annotation => &["Occurrence/body/Exchange/values"],
|
||||
Family::Binding => &["Occurrence/body/ScenarioUse/bindings"],
|
||||
Family::Member(ReferenceCollection::ExchangeItems) => {
|
||||
return [
|
||||
"Interaction",
|
||||
"FunctionalExchange",
|
||||
"BoundaryExchange",
|
||||
"CommunicationMean",
|
||||
]
|
||||
.iter()
|
||||
.map(|kind| format!("/body/{kind}/items"))
|
||||
.find(|p| value.pointer(p).is_some_and(Value::is_array));
|
||||
}
|
||||
Family::Member(ReferenceCollection::TransitionTriggers) => {
|
||||
return value
|
||||
.pointer("/body/StateTransition/triggers")
|
||||
.is_some_and(Value::is_array)
|
||||
.then(|| "/body/StateTransition/triggers".into());
|
||||
}
|
||||
Family::Member(ReferenceCollection::TransitionEffects) => {
|
||||
return value
|
||||
.pointer("/body/StateTransition/effects")
|
||||
.is_some_and(Value::is_array)
|
||||
.then(|| "/body/StateTransition/effects".into());
|
||||
}
|
||||
};
|
||||
prefixes
|
||||
.iter()
|
||||
.flat_map(|prefix| tails.iter().map(move |tail| format!("{prefix}/{tail}")))
|
||||
.find(|p| value.pointer(p).is_some_and(Value::is_array))
|
||||
}
|
||||
pub(super) fn key(facet: &FacetSelector, layer: Layer) -> Option<Value> {
|
||||
match facet {
|
||||
FacetSelector::Member { keys, .. } => if layer == Layer::Oa { keys.oa } else { keys.sa }
|
||||
.map(|id| serde_json::to_value(id).expect("Id")),
|
||||
FacetSelector::Literal(keys) => if layer == Layer::Oa {
|
||||
keys.oa.as_ref()
|
||||
} else {
|
||||
keys.sa.as_ref()
|
||||
}
|
||||
.map(|v| Value::String(v.clone())),
|
||||
FacetSelector::Annotation(keys) => if layer == Layer::Oa {
|
||||
keys.oa.as_ref()
|
||||
} else {
|
||||
keys.sa.as_ref()
|
||||
}
|
||||
.map(|key| serde_json::to_value(key).expect("Annotation key")),
|
||||
FacetSelector::Binding(keys) => if layer == Layer::Oa { keys.oa } else { keys.sa }
|
||||
.map(|id| serde_json::to_value(id).expect("Binding key")),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub(super) fn overlap(a: &FacetSelector, b: &FacetSelector) -> bool {
|
||||
if family(a).is_none() || family(a) != family(b) {
|
||||
return false;
|
||||
}
|
||||
[Layer::Oa, Layer::Sa]
|
||||
.into_iter()
|
||||
.any(|layer| key(a, layer).is_some() && key(a, layer) == key(b, layer))
|
||||
}
|
||||
pub(super) fn project_presence(
|
||||
value: &mut Value,
|
||||
facet: &FacetSelector,
|
||||
layer: Layer,
|
||||
present: bool,
|
||||
) -> Result<(), String> {
|
||||
let family = family(facet).ok_or("This keyed family remains read-only.")?;
|
||||
let key = key(facet, layer)
|
||||
.ok_or("Choose the destination identity in Members before setting or taking presence.")?;
|
||||
let path =
|
||||
path(value, family).ok_or("This collection is not applicable to the destination kind.")?;
|
||||
let list = value
|
||||
.pointer_mut(&path)
|
||||
.and_then(Value::as_array_mut)
|
||||
.ok_or("Destination collection is unavailable.")?;
|
||||
if present {
|
||||
if !list.contains(&key) {
|
||||
list.push(key);
|
||||
}
|
||||
} else {
|
||||
list.retain(|v| v != &key);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl ReviewRow {
|
||||
pub(super) fn keyed_value(
|
||||
&self,
|
||||
project: &Project,
|
||||
trace: &Trace,
|
||||
value: &mut Value,
|
||||
facet: &FacetSelector,
|
||||
decision: SemanticDecision,
|
||||
) -> Result<(), String> {
|
||||
if keyed_rows::supported(facet) {
|
||||
return self.project_row(project, trace, value, facet, decision);
|
||||
}
|
||||
let present = match decision {
|
||||
SemanticDecision::KeepTarget => return Ok(()),
|
||||
SemanticDecision::TakeSource => {
|
||||
// A missing source identity is a target-local row, not a removal.
|
||||
if key(facet, self.direction.source_layer()).is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
let fact = capture_facet(project, trace.oa, trace.sa, facet)?;
|
||||
scalar::value(&fact, self.direction.source_layer()).and_then(|v| v.as_bool()).ok_or("Source collection is not applicable; choose an explicit final presence or Keep.")?
|
||||
}
|
||||
SemanticDecision::Candidate => *self
|
||||
.presence
|
||||
.get(facet)
|
||||
.ok_or("Choose final membership presence.")?,
|
||||
};
|
||||
project_presence(value, facet, self.direction.target_layer(), present)
|
||||
}
|
||||
|
||||
pub(super) fn member_families(&self, project: &Project) -> Vec<Family> {
|
||||
let Some(trace) = self.effective_trace(project) else {
|
||||
return vec![];
|
||||
};
|
||||
let values: Vec<_> = [trace.oa, trace.sa]
|
||||
.into_iter()
|
||||
.filter_map(|id| Draft::from_project(project, id).map(|d| d.value))
|
||||
.collect();
|
||||
FAMILIES
|
||||
.into_iter()
|
||||
.filter(|family| values.iter().any(|value| path(value, *family).is_some()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(super) fn member_choices(&self, project: &Project, layer: Layer) -> Vec<Choice> {
|
||||
if self.member_family == Family::Binding {
|
||||
return self.binding_choices(project, layer, true);
|
||||
}
|
||||
let Some(trace) = self.effective_trace(project) else {
|
||||
return vec![];
|
||||
};
|
||||
let id = if layer == Layer::Oa {
|
||||
trace.oa
|
||||
} else {
|
||||
trace.sa
|
||||
};
|
||||
let Some(mut draft) = Draft::from_project(project, id) else {
|
||||
return vec![];
|
||||
};
|
||||
if layer == self.direction.target_layer() {
|
||||
draft.set_context(self.combined_value(project, true).ok());
|
||||
}
|
||||
let selected = if layer == Layer::Oa {
|
||||
&self.member_oa
|
||||
} else {
|
||||
&self.member_sa
|
||||
};
|
||||
let Some(path) = path(&draft.context_value(), self.member_family) else {
|
||||
return selected.iter().cloned().collect();
|
||||
};
|
||||
crate::forms::reference_choices(
|
||||
&draft,
|
||||
project,
|
||||
&format!("{path}/0"),
|
||||
selected.as_ref().map_or(Id::nil(), |c| c.id),
|
||||
)
|
||||
.into_iter()
|
||||
.filter(|c| !c.id.is_nil())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(super) fn edit_pair(
|
||||
&mut self,
|
||||
project: &Project,
|
||||
facet: FacetSelector,
|
||||
) -> Result<(), String> {
|
||||
self.member_family = family(&facet).ok_or("This keyed family remains read-only.")?;
|
||||
match facet {
|
||||
FacetSelector::Annotation(keys) => {
|
||||
self.annotation_oa.selected = keys.oa.map(|key| crate::data_values::FieldPathKey {
|
||||
path: key.path,
|
||||
field: key.field,
|
||||
});
|
||||
self.annotation_sa.selected = keys.sa.map(|key| crate::data_values::FieldPathKey {
|
||||
path: key.path,
|
||||
field: key.field,
|
||||
});
|
||||
}
|
||||
FacetSelector::Binding(keys) => {
|
||||
self.member_oa = keys.oa.map(|id| view::choice(project, id));
|
||||
self.member_sa = keys.sa.map(|id| view::choice(project, id));
|
||||
}
|
||||
FacetSelector::Member { keys, .. } => {
|
||||
self.member_oa = keys.oa.map(|id| view::choice(project, id));
|
||||
self.member_sa = keys.sa.map(|id| view::choice(project, id));
|
||||
}
|
||||
FacetSelector::Literal(keys) => {
|
||||
self.literal_oa = keys.oa.is_some();
|
||||
self.literal_sa = keys.sa.is_some();
|
||||
self.literal = keys.oa.or(keys.sa).unwrap_or_default();
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
self.tab = Tab::Members;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn stage_member(&mut self, project: &Project) -> Result<(), String> {
|
||||
if !FAMILIES.contains(&self.member_family) {
|
||||
return Err("This keyed family remains read-only.".into());
|
||||
}
|
||||
let facet = match self.member_family {
|
||||
Family::Member(collection) => FacetSelector::Member {
|
||||
collection,
|
||||
keys: KeyPair {
|
||||
oa: self.member_oa.as_ref().map(|c| c.id),
|
||||
sa: self.member_sa.as_ref().map(|c| c.id),
|
||||
},
|
||||
},
|
||||
Family::Literal => FacetSelector::Literal(KeyPair {
|
||||
oa: self.literal_oa.then(|| self.literal.clone()),
|
||||
sa: self.literal_sa.then(|| self.literal.clone()),
|
||||
}),
|
||||
Family::Annotation => FacetSelector::Annotation(KeyPair {
|
||||
oa: self
|
||||
.annotation_oa
|
||||
.selected
|
||||
.as_ref()
|
||||
.map(|key| AnnotationKey {
|
||||
path: key.path.clone(),
|
||||
field: key.field,
|
||||
}),
|
||||
sa: self
|
||||
.annotation_sa
|
||||
.selected
|
||||
.as_ref()
|
||||
.map(|key| AnnotationKey {
|
||||
path: key.path.clone(),
|
||||
field: key.field,
|
||||
}),
|
||||
}),
|
||||
Family::Binding => FacetSelector::Binding(KeyPair {
|
||||
oa: self.member_oa.as_ref().map(|c| c.id),
|
||||
sa: self.member_sa.as_ref().map(|c| c.id),
|
||||
}),
|
||||
};
|
||||
let trace = self.effective_trace(project).ok_or("Choose a trace.")?;
|
||||
capture_facet(project, trace.oa, trace.sa, &facet)?;
|
||||
self.stage_row_references(&facet)?;
|
||||
if let FacetSelector::Member {
|
||||
keys:
|
||||
KeyPair {
|
||||
oa: Some(oa),
|
||||
sa: Some(sa),
|
||||
},
|
||||
..
|
||||
} = &facet
|
||||
{
|
||||
self.references.insert(*oa, *sa);
|
||||
}
|
||||
self.staged.insert(facet.clone());
|
||||
self.selected = Some(facet);
|
||||
self.tab = Tab::Facts;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,345 +1,345 @@
|
||||
//! Atomic message-value and lifeline-binding rows with exact, immutable keys.
|
||||
use super::*;
|
||||
use crate::data_values::{FieldPathChoice, FieldPathKey};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct AddressDraft {
|
||||
pub selected: Option<FieldPathKey>,
|
||||
pub scope: Option<FieldPathKey>,
|
||||
pub page: usize,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RowDraft {
|
||||
pub expression: String,
|
||||
pub editor: text_editor::Content,
|
||||
pub local: Option<Choice>,
|
||||
}
|
||||
|
||||
pub(super) fn supported(facet: &FacetSelector) -> bool {
|
||||
matches!(
|
||||
facet,
|
||||
FacetSelector::Annotation(_) | FacetSelector::Binding(_)
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn target_selector(facet: &FacetSelector, layer: Layer) -> FacetSelector {
|
||||
match facet {
|
||||
FacetSelector::Annotation(keys) => FacetSelector::Annotation(KeyPair {
|
||||
oa: (layer == Layer::Oa).then(|| keys.oa.clone()).flatten(),
|
||||
sa: (layer == Layer::Sa).then(|| keys.sa.clone()).flatten(),
|
||||
}),
|
||||
FacetSelector::Binding(keys) => FacetSelector::Binding(KeyPair {
|
||||
oa: if layer == Layer::Oa { keys.oa } else { None },
|
||||
sa: if layer == Layer::Sa { keys.sa } else { None },
|
||||
}),
|
||||
_ => facet.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
impl ReviewRow {
|
||||
pub(super) fn annotation_address(&self, layer: Layer) -> &AddressDraft {
|
||||
if layer == Layer::Oa {
|
||||
&self.annotation_oa
|
||||
} else {
|
||||
&self.annotation_sa
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn annotation_address_mut(&mut self, layer: Layer) -> &mut AddressDraft {
|
||||
if layer == Layer::Oa {
|
||||
&mut self.annotation_oa
|
||||
} else {
|
||||
&mut self.annotation_sa
|
||||
}
|
||||
}
|
||||
|
||||
fn row_owner(&self, project: &Project, layer: Layer) -> Option<Element> {
|
||||
let trace = self.effective_trace(project)?;
|
||||
let id = if layer == Layer::Oa {
|
||||
trace.oa
|
||||
} else {
|
||||
trace.sa
|
||||
};
|
||||
if layer == self.direction.target_layer()
|
||||
&& let Ok(value) = self.combined_value(project, true)
|
||||
&& let Ok(element) = serde_json::from_value(value)
|
||||
{
|
||||
return Some(element);
|
||||
}
|
||||
project.elements.get(&id).cloned()
|
||||
}
|
||||
|
||||
pub(super) fn annotation_choices(
|
||||
&self,
|
||||
project: &Project,
|
||||
layer: Layer,
|
||||
) -> Result<Vec<FieldPathChoice>, String> {
|
||||
let element = self.row_owner(project, layer).ok_or("Choose a trace.")?;
|
||||
let Some(SharedBody::Occurrence {
|
||||
body:
|
||||
OccurrenceBody::Exchange {
|
||||
exchange: Some(exchange),
|
||||
message_kind,
|
||||
..
|
||||
},
|
||||
..
|
||||
}) = element.shared()
|
||||
else {
|
||||
return Err(
|
||||
"Choose an exchange on this message before selecting a carried field.".into(),
|
||||
);
|
||||
};
|
||||
let exchange = project
|
||||
.relations
|
||||
.get(exchange)
|
||||
.ok_or("The message exchange is unavailable.")?;
|
||||
crate::data_values::at_scope(
|
||||
project,
|
||||
exchange,
|
||||
message_kind,
|
||||
self.annotation_address(layer).scope.as_ref(),
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn binding_choices(
|
||||
&self,
|
||||
project: &Project,
|
||||
layer: Layer,
|
||||
referenced: bool,
|
||||
) -> Vec<Choice> {
|
||||
let Some(element) = self.row_owner(project, layer) else {
|
||||
return vec![];
|
||||
};
|
||||
let scenario = if referenced {
|
||||
match element.shared() {
|
||||
Some(SharedBody::Occurrence {
|
||||
body: OccurrenceBody::ScenarioUse { scenario, .. },
|
||||
..
|
||||
}) => *scenario,
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
element
|
||||
.parent
|
||||
.and_then(|parent| validation::ancestor_of_kind(project, parent, Kind::Scenario))
|
||||
};
|
||||
let mut choices: Vec<_> = project
|
||||
.elements
|
||||
.values()
|
||||
.filter(|e| {
|
||||
e.kind() == Kind::Lifeline
|
||||
&& e.layer() == layer
|
||||
&& scenario.is_some()
|
||||
&& e.parent == scenario
|
||||
})
|
||||
.map(|e| view::choice(project, e.id))
|
||||
.collect();
|
||||
choices.sort_by_cached_key(|c| c.label.to_lowercase());
|
||||
choices
|
||||
}
|
||||
|
||||
pub(super) fn stage_row_references(&mut self, facet: &FacetSelector) -> Result<(), String> {
|
||||
let pairs: Vec<_> = match facet {
|
||||
FacetSelector::Annotation(KeyPair {
|
||||
oa: Some(oa),
|
||||
sa: Some(sa),
|
||||
}) => {
|
||||
if oa.path.len() != sa.path.len() {
|
||||
return Err("Paired annotation paths must have the same depth; review distinct addresses separately.".into());
|
||||
}
|
||||
oa.path
|
||||
.iter()
|
||||
.chain(std::iter::once(&oa.field))
|
||||
.copied()
|
||||
.zip(sa.path.iter().chain(std::iter::once(&sa.field)).copied())
|
||||
.collect()
|
||||
}
|
||||
FacetSelector::Binding(KeyPair {
|
||||
oa: Some(oa),
|
||||
sa: Some(sa),
|
||||
}) => vec![(*oa, *sa)],
|
||||
_ => vec![],
|
||||
};
|
||||
// A repeated recursive field may occur several times in one address.
|
||||
// Never let the last occurrence silently replace a conflicting pair.
|
||||
let mut supplied = BTreeMap::new();
|
||||
for (oa, sa) in pairs {
|
||||
if supplied.insert(oa, sa).is_some_and(|prior| prior != sa) {
|
||||
return Err(
|
||||
"This address pairs the same Operational field with different System fields."
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
self.references.extend(supplied);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn initialize_row(&mut self, project: &Project, facet: &FacetSelector) {
|
||||
if self.row_drafts.contains_key(facet) {
|
||||
return;
|
||||
}
|
||||
let value = self
|
||||
.effective_trace(project)
|
||||
.and_then(|t| capture_facet(project, t.oa, t.sa, facet).ok())
|
||||
.and_then(|f| scalar::value(&f, self.direction.target_layer()));
|
||||
let present = value.as_ref().is_some_and(|v| !v.is_null());
|
||||
let mut draft = RowDraft::default();
|
||||
if let Some(value) = value {
|
||||
draft.expression = value["expression"].as_str().unwrap_or_default().into();
|
||||
draft.editor = text_editor::Content::with_text(&draft.expression);
|
||||
draft.local = value["local"]
|
||||
.as_str()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.map(|id| view::choice(project, id));
|
||||
}
|
||||
self.presence.entry(facet.clone()).or_insert(present);
|
||||
self.row_drafts.insert(facet.clone(), draft);
|
||||
}
|
||||
|
||||
pub(super) fn edit_row(
|
||||
&mut self,
|
||||
facet: &FacetSelector,
|
||||
action: impl FnOnce(&mut RowDraft),
|
||||
) -> Result<(), String> {
|
||||
if !supported(facet)
|
||||
|| self.selected.as_ref() != Some(facet)
|
||||
|| self.decisions.get(facet) != Some(&SemanticDecision::Candidate)
|
||||
{
|
||||
return Err("Select this row and choose Set final value before editing it.".into());
|
||||
}
|
||||
action(
|
||||
self.row_drafts
|
||||
.get_mut(facet)
|
||||
.ok_or("The row draft is unavailable.")?,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn project_row(
|
||||
&self,
|
||||
project: &Project,
|
||||
trace: &Trace,
|
||||
value: &mut Value,
|
||||
facet: &FacetSelector,
|
||||
decision: SemanticDecision,
|
||||
) -> Result<(), String> {
|
||||
if decision == SemanticDecision::KeepTarget {
|
||||
return Ok(());
|
||||
}
|
||||
let source_layer = self.direction.source_layer();
|
||||
let target_layer = self.direction.target_layer();
|
||||
if decision == SemanticDecision::TakeSource && keyed::key(facet, source_layer).is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
let target_key = keyed::key(facet, target_layer)
|
||||
.ok_or("Choose the destination row address before setting or taking its value.")?;
|
||||
let row = if decision == SemanticDecision::Candidate {
|
||||
if !self.presence.get(facet).copied().unwrap_or(false) {
|
||||
Value::Null
|
||||
} else {
|
||||
let draft = self
|
||||
.row_drafts
|
||||
.get(facet)
|
||||
.ok_or("Set the final row value.")?;
|
||||
match facet {
|
||||
FacetSelector::Annotation(_) => {
|
||||
let key: AnnotationKey = serde_json::from_value(target_key.clone())
|
||||
.map_err(|e| e.to_string())?;
|
||||
serde_json::to_value(MessageValue {
|
||||
path: key.path,
|
||||
field: key.field,
|
||||
expression: draft.expression.clone(),
|
||||
})
|
||||
.expect("Message value")
|
||||
}
|
||||
FacetSelector::Binding(_) => {
|
||||
serde_json::json!({"referenced": target_key, "local": draft.local.as_ref().ok_or("Choose the destination scenario's local lifeline.")?.id})
|
||||
}
|
||||
_ => return Err("Unsupported row.".into()),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let fact = capture_facet(project, trace.oa, trace.sa, facet)?;
|
||||
let row = scalar::value(&fact, source_layer)
|
||||
.ok_or("Source row collection is not applicable; review its constructor first.")?;
|
||||
if row.is_null() {
|
||||
row
|
||||
} else {
|
||||
let map = |id: Id| -> Result<Id, String> {
|
||||
if source_layer == Layer::Oa {
|
||||
self.references.get(&id).copied().ok_or_else(|| {
|
||||
format!("Choose a reference counterpart for {}.", project.named(id))
|
||||
})
|
||||
} else {
|
||||
let matches: Vec<_> = self
|
||||
.references
|
||||
.iter()
|
||||
.filter(|(_, sa)| **sa == id)
|
||||
.map(|(oa, _)| *oa)
|
||||
.collect();
|
||||
if matches.len() == 1 {
|
||||
Ok(matches[0])
|
||||
} else {
|
||||
Err(format!(
|
||||
"Choose one unambiguous reference counterpart for {}.",
|
||||
project.named(id)
|
||||
))
|
||||
}
|
||||
}
|
||||
};
|
||||
match facet {
|
||||
FacetSelector::Annotation(_) => {
|
||||
let mut row: MessageValue =
|
||||
serde_json::from_value(row).map_err(|e| e.to_string())?;
|
||||
row.path = row.path.into_iter().map(map).collect::<Result<_, _>>()?;
|
||||
row.field = map(row.field)?;
|
||||
if serde_json::to_value(AnnotationKey::from(&row)).expect("key")
|
||||
!= target_key
|
||||
{
|
||||
return Err("Mapped annotation address disagrees with the selected destination address.".into());
|
||||
}
|
||||
serde_json::to_value(row).expect("row")
|
||||
}
|
||||
FacetSelector::Binding(_) => {
|
||||
let row: LifelineBinding =
|
||||
serde_json::from_value(row).map_err(|e| e.to_string())?;
|
||||
let mapped = LifelineBinding {
|
||||
referenced: map(row.referenced)?,
|
||||
local: map(row.local)?,
|
||||
};
|
||||
if serde_json::to_value(mapped.referenced).expect("Id") != target_key {
|
||||
return Err("Mapped binding key disagrees with the selected destination lifeline.".into());
|
||||
}
|
||||
serde_json::to_value(mapped).expect("row")
|
||||
}
|
||||
_ => return Err("Unsupported row.".into()),
|
||||
}
|
||||
}
|
||||
};
|
||||
let family = keyed::family(facet).ok_or("Unsupported row.")?;
|
||||
let path = keyed::path(value, family)
|
||||
.ok_or("The destination constructor has no collection for this row.")?;
|
||||
let rows = value
|
||||
.pointer_mut(&path)
|
||||
.and_then(Value::as_array_mut)
|
||||
.ok_or("Destination rows are unavailable.")?;
|
||||
let position = rows.iter().position(|row| match facet {
|
||||
FacetSelector::Annotation(_) => {
|
||||
serde_json::json!({"path": row["path"], "field": row["field"]}) == target_key
|
||||
}
|
||||
FacetSelector::Binding(_) => row["referenced"] == target_key,
|
||||
_ => false,
|
||||
});
|
||||
match (position, row.is_null()) {
|
||||
(Some(index), false) => rows[index] = row,
|
||||
(Some(index), true) => {
|
||||
rows.remove(index);
|
||||
}
|
||||
(None, false) => rows.push(row),
|
||||
(None, true) => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
//! Atomic message-value and lifeline-binding rows with exact, immutable keys.
|
||||
use super::*;
|
||||
use crate::data_values::{FieldPathChoice, FieldPathKey};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct AddressDraft {
|
||||
pub selected: Option<FieldPathKey>,
|
||||
pub scope: Option<FieldPathKey>,
|
||||
pub page: usize,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RowDraft {
|
||||
pub expression: String,
|
||||
pub editor: text_editor::Content,
|
||||
pub local: Option<Choice>,
|
||||
}
|
||||
|
||||
pub(super) fn supported(facet: &FacetSelector) -> bool {
|
||||
matches!(
|
||||
facet,
|
||||
FacetSelector::Annotation(_) | FacetSelector::Binding(_)
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn target_selector(facet: &FacetSelector, layer: Layer) -> FacetSelector {
|
||||
match facet {
|
||||
FacetSelector::Annotation(keys) => FacetSelector::Annotation(KeyPair {
|
||||
oa: (layer == Layer::Oa).then(|| keys.oa.clone()).flatten(),
|
||||
sa: (layer == Layer::Sa).then(|| keys.sa.clone()).flatten(),
|
||||
}),
|
||||
FacetSelector::Binding(keys) => FacetSelector::Binding(KeyPair {
|
||||
oa: if layer == Layer::Oa { keys.oa } else { None },
|
||||
sa: if layer == Layer::Sa { keys.sa } else { None },
|
||||
}),
|
||||
_ => facet.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
impl ReviewRow {
|
||||
pub(super) fn annotation_address(&self, layer: Layer) -> &AddressDraft {
|
||||
if layer == Layer::Oa {
|
||||
&self.annotation_oa
|
||||
} else {
|
||||
&self.annotation_sa
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn annotation_address_mut(&mut self, layer: Layer) -> &mut AddressDraft {
|
||||
if layer == Layer::Oa {
|
||||
&mut self.annotation_oa
|
||||
} else {
|
||||
&mut self.annotation_sa
|
||||
}
|
||||
}
|
||||
|
||||
fn row_owner(&self, project: &Project, layer: Layer) -> Option<Element> {
|
||||
let trace = self.effective_trace(project)?;
|
||||
let id = if layer == Layer::Oa {
|
||||
trace.oa
|
||||
} else {
|
||||
trace.sa
|
||||
};
|
||||
if layer == self.direction.target_layer()
|
||||
&& let Ok(value) = self.combined_value(project, true)
|
||||
&& let Ok(element) = serde_json::from_value(value)
|
||||
{
|
||||
return Some(element);
|
||||
}
|
||||
project.elements.get(&id).cloned()
|
||||
}
|
||||
|
||||
pub(super) fn annotation_choices(
|
||||
&self,
|
||||
project: &Project,
|
||||
layer: Layer,
|
||||
) -> Result<Vec<FieldPathChoice>, String> {
|
||||
let element = self.row_owner(project, layer).ok_or("Choose a trace.")?;
|
||||
let Some(SharedBody::Occurrence {
|
||||
body:
|
||||
OccurrenceBody::Exchange {
|
||||
exchange: Some(exchange),
|
||||
message_kind,
|
||||
..
|
||||
},
|
||||
..
|
||||
}) = element.shared()
|
||||
else {
|
||||
return Err(
|
||||
"Choose an exchange on this message before selecting a carried field.".into(),
|
||||
);
|
||||
};
|
||||
let exchange = project
|
||||
.relations
|
||||
.get(exchange)
|
||||
.ok_or("The message exchange is unavailable.")?;
|
||||
crate::data_values::at_scope(
|
||||
project,
|
||||
exchange,
|
||||
message_kind,
|
||||
self.annotation_address(layer).scope.as_ref(),
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn binding_choices(
|
||||
&self,
|
||||
project: &Project,
|
||||
layer: Layer,
|
||||
referenced: bool,
|
||||
) -> Vec<Choice> {
|
||||
let Some(element) = self.row_owner(project, layer) else {
|
||||
return vec![];
|
||||
};
|
||||
let scenario = if referenced {
|
||||
match element.shared() {
|
||||
Some(SharedBody::Occurrence {
|
||||
body: OccurrenceBody::ScenarioUse { scenario, .. },
|
||||
..
|
||||
}) => *scenario,
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
element
|
||||
.parent
|
||||
.and_then(|parent| validation::ancestor_of_kind(project, parent, Kind::Scenario))
|
||||
};
|
||||
let mut choices: Vec<_> = project
|
||||
.elements
|
||||
.values()
|
||||
.filter(|e| {
|
||||
e.kind() == Kind::Lifeline
|
||||
&& e.layer() == layer
|
||||
&& scenario.is_some()
|
||||
&& e.parent == scenario
|
||||
})
|
||||
.map(|e| view::choice(project, e.id))
|
||||
.collect();
|
||||
choices.sort_by_cached_key(|c| c.label.to_lowercase());
|
||||
choices
|
||||
}
|
||||
|
||||
pub(super) fn stage_row_references(&mut self, facet: &FacetSelector) -> Result<(), String> {
|
||||
let pairs: Vec<_> = match facet {
|
||||
FacetSelector::Annotation(KeyPair {
|
||||
oa: Some(oa),
|
||||
sa: Some(sa),
|
||||
}) => {
|
||||
if oa.path.len() != sa.path.len() {
|
||||
return Err("Paired annotation paths must have the same depth; review distinct addresses separately.".into());
|
||||
}
|
||||
oa.path
|
||||
.iter()
|
||||
.chain(std::iter::once(&oa.field))
|
||||
.copied()
|
||||
.zip(sa.path.iter().chain(std::iter::once(&sa.field)).copied())
|
||||
.collect()
|
||||
}
|
||||
FacetSelector::Binding(KeyPair {
|
||||
oa: Some(oa),
|
||||
sa: Some(sa),
|
||||
}) => vec![(*oa, *sa)],
|
||||
_ => vec![],
|
||||
};
|
||||
// A repeated recursive field may occur several times in one address.
|
||||
// Never let the last occurrence silently replace a conflicting pair.
|
||||
let mut supplied = BTreeMap::new();
|
||||
for (oa, sa) in pairs {
|
||||
if supplied.insert(oa, sa).is_some_and(|prior| prior != sa) {
|
||||
return Err(
|
||||
"This address pairs the same Operational field with different System fields."
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
self.references.extend(supplied);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn initialize_row(&mut self, project: &Project, facet: &FacetSelector) {
|
||||
if self.row_drafts.contains_key(facet) {
|
||||
return;
|
||||
}
|
||||
let value = self
|
||||
.effective_trace(project)
|
||||
.and_then(|t| capture_facet(project, t.oa, t.sa, facet).ok())
|
||||
.and_then(|f| scalar::value(&f, self.direction.target_layer()));
|
||||
let present = value.as_ref().is_some_and(|v| !v.is_null());
|
||||
let mut draft = RowDraft::default();
|
||||
if let Some(value) = value {
|
||||
draft.expression = value["expression"].as_str().unwrap_or_default().into();
|
||||
draft.editor = text_editor::Content::with_text(&draft.expression);
|
||||
draft.local = value["local"]
|
||||
.as_str()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.map(|id| view::choice(project, id));
|
||||
}
|
||||
self.presence.entry(facet.clone()).or_insert(present);
|
||||
self.row_drafts.insert(facet.clone(), draft);
|
||||
}
|
||||
|
||||
pub(super) fn edit_row(
|
||||
&mut self,
|
||||
facet: &FacetSelector,
|
||||
action: impl FnOnce(&mut RowDraft),
|
||||
) -> Result<(), String> {
|
||||
if !supported(facet)
|
||||
|| self.selected.as_ref() != Some(facet)
|
||||
|| self.decisions.get(facet) != Some(&SemanticDecision::Candidate)
|
||||
{
|
||||
return Err("Select this row and choose Set final value before editing it.".into());
|
||||
}
|
||||
action(
|
||||
self.row_drafts
|
||||
.get_mut(facet)
|
||||
.ok_or("The row draft is unavailable.")?,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn project_row(
|
||||
&self,
|
||||
project: &Project,
|
||||
trace: &Trace,
|
||||
value: &mut Value,
|
||||
facet: &FacetSelector,
|
||||
decision: SemanticDecision,
|
||||
) -> Result<(), String> {
|
||||
if decision == SemanticDecision::KeepTarget {
|
||||
return Ok(());
|
||||
}
|
||||
let source_layer = self.direction.source_layer();
|
||||
let target_layer = self.direction.target_layer();
|
||||
if decision == SemanticDecision::TakeSource && keyed::key(facet, source_layer).is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
let target_key = keyed::key(facet, target_layer)
|
||||
.ok_or("Choose the destination row address before setting or taking its value.")?;
|
||||
let row = if decision == SemanticDecision::Candidate {
|
||||
if !self.presence.get(facet).copied().unwrap_or(false) {
|
||||
Value::Null
|
||||
} else {
|
||||
let draft = self
|
||||
.row_drafts
|
||||
.get(facet)
|
||||
.ok_or("Set the final row value.")?;
|
||||
match facet {
|
||||
FacetSelector::Annotation(_) => {
|
||||
let key: AnnotationKey = serde_json::from_value(target_key.clone())
|
||||
.map_err(|e| e.to_string())?;
|
||||
serde_json::to_value(MessageValue {
|
||||
path: key.path,
|
||||
field: key.field,
|
||||
expression: draft.expression.clone(),
|
||||
})
|
||||
.expect("Message value")
|
||||
}
|
||||
FacetSelector::Binding(_) => {
|
||||
serde_json::json!({"referenced": target_key, "local": draft.local.as_ref().ok_or("Choose the destination scenario's local lifeline.")?.id})
|
||||
}
|
||||
_ => return Err("Unsupported row.".into()),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let fact = capture_facet(project, trace.oa, trace.sa, facet)?;
|
||||
let row = scalar::value(&fact, source_layer)
|
||||
.ok_or("Source row collection is not applicable; review its constructor first.")?;
|
||||
if row.is_null() {
|
||||
row
|
||||
} else {
|
||||
let map = |id: Id| -> Result<Id, String> {
|
||||
if source_layer == Layer::Oa {
|
||||
self.references.get(&id).copied().ok_or_else(|| {
|
||||
format!("Choose a reference counterpart for {}.", project.named(id))
|
||||
})
|
||||
} else {
|
||||
let matches: Vec<_> = self
|
||||
.references
|
||||
.iter()
|
||||
.filter(|(_, sa)| **sa == id)
|
||||
.map(|(oa, _)| *oa)
|
||||
.collect();
|
||||
if matches.len() == 1 {
|
||||
Ok(matches[0])
|
||||
} else {
|
||||
Err(format!(
|
||||
"Choose one unambiguous reference counterpart for {}.",
|
||||
project.named(id)
|
||||
))
|
||||
}
|
||||
}
|
||||
};
|
||||
match facet {
|
||||
FacetSelector::Annotation(_) => {
|
||||
let mut row: MessageValue =
|
||||
serde_json::from_value(row).map_err(|e| e.to_string())?;
|
||||
row.path = row.path.into_iter().map(map).collect::<Result<_, _>>()?;
|
||||
row.field = map(row.field)?;
|
||||
if serde_json::to_value(AnnotationKey::from(&row)).expect("key")
|
||||
!= target_key
|
||||
{
|
||||
return Err("Mapped annotation address disagrees with the selected destination address.".into());
|
||||
}
|
||||
serde_json::to_value(row).expect("row")
|
||||
}
|
||||
FacetSelector::Binding(_) => {
|
||||
let row: LifelineBinding =
|
||||
serde_json::from_value(row).map_err(|e| e.to_string())?;
|
||||
let mapped = LifelineBinding {
|
||||
referenced: map(row.referenced)?,
|
||||
local: map(row.local)?,
|
||||
};
|
||||
if serde_json::to_value(mapped.referenced).expect("Id") != target_key {
|
||||
return Err("Mapped binding key disagrees with the selected destination lifeline.".into());
|
||||
}
|
||||
serde_json::to_value(mapped).expect("row")
|
||||
}
|
||||
_ => return Err("Unsupported row.".into()),
|
||||
}
|
||||
}
|
||||
};
|
||||
let family = keyed::family(facet).ok_or("Unsupported row.")?;
|
||||
let path = keyed::path(value, family)
|
||||
.ok_or("The destination constructor has no collection for this row.")?;
|
||||
let rows = value
|
||||
.pointer_mut(&path)
|
||||
.and_then(Value::as_array_mut)
|
||||
.ok_or("Destination rows are unavailable.")?;
|
||||
let position = rows.iter().position(|row| match facet {
|
||||
FacetSelector::Annotation(_) => {
|
||||
serde_json::json!({"path": row["path"], "field": row["field"]}) == target_key
|
||||
}
|
||||
FacetSelector::Binding(_) => row["referenced"] == target_key,
|
||||
_ => false,
|
||||
});
|
||||
match (position, row.is_null()) {
|
||||
(Some(index), false) => rows[index] = row,
|
||||
(Some(index), true) => {
|
||||
rows.remove(index);
|
||||
}
|
||||
(None, false) => rows.push(row),
|
||||
(None, true) => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,254 +1,254 @@
|
||||
//! Independent membership and complete final order; the core validates every proposal.
|
||||
use super::*;
|
||||
|
||||
pub(super) fn collection(which: OrderedCollection) -> ReferenceCollection {
|
||||
match which {
|
||||
OrderedCollection::Steps => ReferenceCollection::Steps,
|
||||
OrderedCollection::FragmentBranches => ReferenceCollection::FragmentBranches,
|
||||
}
|
||||
}
|
||||
pub(super) fn ordered(which: ReferenceCollection) -> Option<OrderedCollection> {
|
||||
match which {
|
||||
ReferenceCollection::Steps => Some(OrderedCollection::Steps),
|
||||
ReferenceCollection::FragmentBranches => Some(OrderedCollection::FragmentBranches),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub(super) fn read(value: &Value, which: OrderedCollection) -> Result<Vec<Id>, String> {
|
||||
let path = keyed::path(value, keyed::Family::Member(collection(which)))
|
||||
.ok_or("This ordered collection is unavailable on the destination.")?;
|
||||
serde_json::from_value(value.pointer(&path).unwrap().clone()).map_err(|e| e.to_string())
|
||||
}
|
||||
fn write(value: &mut Value, which: OrderedCollection, ids: Vec<Id>) -> Result<(), String> {
|
||||
let path = keyed::path(value, keyed::Family::Member(collection(which)))
|
||||
.ok_or("This ordered collection is unavailable on the destination.")?;
|
||||
*value.pointer_mut(&path).unwrap() = serde_json::to_value(ids).expect("Ordered identities");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl ReviewRow {
|
||||
pub(super) fn ordered_members(
|
||||
&self,
|
||||
project: &Project,
|
||||
which: OrderedCollection,
|
||||
) -> Result<Vec<Id>, String> {
|
||||
read(&self.combine(project, true, true)?, which)
|
||||
}
|
||||
|
||||
pub(super) fn initialize_order(
|
||||
&mut self,
|
||||
project: &Project,
|
||||
which: OrderedCollection,
|
||||
) -> Result<(), String> {
|
||||
if !self.order_drafts.contains_key(&which) {
|
||||
let ids = self.ordered_members(project, which)?;
|
||||
self.order_drafts.insert(which, ids);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn reconcile_order(
|
||||
&mut self,
|
||||
project: &Project,
|
||||
which: OrderedCollection,
|
||||
) -> Result<(), String> {
|
||||
self.require_order_candidate(which)?;
|
||||
let members = self.ordered_members(project, which)?;
|
||||
let draft = self
|
||||
.order_drafts
|
||||
.get_mut(&which)
|
||||
.ok_or("Choose a final order first.")?;
|
||||
// This is an explicit user action. Ordinary refresh never discards an authored order.
|
||||
let mut seen = BTreeSet::new();
|
||||
draft.retain(|id| members.contains(id) && seen.insert(*id));
|
||||
for id in members {
|
||||
if seen.insert(id) {
|
||||
draft.push(id);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn require_order_candidate(&self, which: OrderedCollection) -> Result<(), String> {
|
||||
let facet = FacetSelector::Order(which);
|
||||
if self.selected.as_ref() != Some(&facet)
|
||||
|| self.decisions.get(&facet) != Some(&SemanticDecision::Candidate)
|
||||
{
|
||||
return Err("Select Set final value for this order before changing it.".into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn move_order(
|
||||
&mut self,
|
||||
which: OrderedCollection,
|
||||
index: usize,
|
||||
delta: i32,
|
||||
) -> Result<(), String> {
|
||||
self.require_order_candidate(which)?;
|
||||
let ids = self
|
||||
.order_drafts
|
||||
.get_mut(&which)
|
||||
.ok_or("Choose a final order first.")?;
|
||||
let destination = match delta {
|
||||
-1 => index.checked_sub(1),
|
||||
1 => index.checked_add(1),
|
||||
_ => None,
|
||||
};
|
||||
let destination = destination
|
||||
.filter(|i| index < ids.len() && *i < ids.len())
|
||||
.ok_or("Choose an existing adjacent position in the final order.")?;
|
||||
ids.swap(index, destination);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn project_order(
|
||||
&self,
|
||||
project: &Project,
|
||||
trace: &Trace,
|
||||
value: &mut Value,
|
||||
which: OrderedCollection,
|
||||
decision: SemanticDecision,
|
||||
) -> Result<(), String> {
|
||||
let current = read(value, which)?;
|
||||
let supplied = match decision {
|
||||
SemanticDecision::KeepTarget => return Ok(()),
|
||||
SemanticDecision::Candidate => self
|
||||
.order_drafts
|
||||
.get(&which)
|
||||
.cloned()
|
||||
.ok_or("Choose a final order.")?,
|
||||
SemanticDecision::TakeSource => {
|
||||
let fact =
|
||||
capture_facet(project, trace.oa, trace.sa, &FacetSelector::Order(which))?;
|
||||
let source: Vec<Id> = serde_json::from_value(
|
||||
scalar::value(&fact, self.direction.source_layer())
|
||||
.ok_or("The source has no applicable order.")?,
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let map = self.order_reference_map()?;
|
||||
source
|
||||
.into_iter()
|
||||
.map(|id| {
|
||||
map.get(&id).copied().ok_or_else(|| {
|
||||
format!(
|
||||
"Pair source order member {} explicitly in Reference pairs.",
|
||||
project.named(id)
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
}
|
||||
};
|
||||
let actual: BTreeSet<_> = current.iter().copied().collect();
|
||||
let desired: BTreeSet<_> = supplied.iter().copied().collect();
|
||||
if desired.len() != supplied.len() {
|
||||
return Err(
|
||||
"The final order contains a duplicate identity; reconcile its members explicitly."
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
if desired != actual {
|
||||
if decision == SemanticDecision::TakeSource
|
||||
&& desired.is_subset(&actual)
|
||||
&& current
|
||||
.iter()
|
||||
.filter(|id| desired.contains(id))
|
||||
.copied()
|
||||
.collect::<Vec<_>>()
|
||||
== supplied
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
return Err("Final order must contain every surviving destination member exactly once. Reconcile members, then choose their positions; membership is reviewed separately.".into());
|
||||
}
|
||||
write(value, which, supplied)
|
||||
}
|
||||
|
||||
fn order_reference_map(&self) -> Result<BTreeMap<Id, Id>, String> {
|
||||
let mut map = BTreeMap::new();
|
||||
for (oa, sa) in &self.references {
|
||||
let (a, b) = if self.direction == TransitionDirection::OaToSa {
|
||||
(*oa, *sa)
|
||||
} else {
|
||||
(*sa, *oa)
|
||||
};
|
||||
if map.insert(a, b).is_some_and(|old| old != b) {
|
||||
return Err("Reverse reference correspondence is ambiguous.".into());
|
||||
}
|
||||
}
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
pub(super) fn project_ordered_member(
|
||||
&self,
|
||||
project: &Project,
|
||||
trace: &Trace,
|
||||
value: &mut Value,
|
||||
facet: &FacetSelector,
|
||||
decision: SemanticDecision,
|
||||
form_context: bool,
|
||||
) -> Result<(), String> {
|
||||
let FacetSelector::Member {
|
||||
collection: member_collection,
|
||||
keys,
|
||||
} = facet
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
let which = ordered(*member_collection).expect("Ordered member");
|
||||
let before = read(value, which)?;
|
||||
self.keyed_value(project, trace, value, facet, decision)?;
|
||||
let after = read(value, which)?;
|
||||
let explicit = self
|
||||
.decisions
|
||||
.get(&FacetSelector::Order(which))
|
||||
.is_some_and(|d| *d != SemanticDecision::KeepTarget);
|
||||
if decision != SemanticDecision::TakeSource || explicit || after.len() <= before.len() {
|
||||
return Ok(());
|
||||
}
|
||||
let (source_key, target_key) = if self.direction == TransitionDirection::OaToSa {
|
||||
(keys.oa, keys.sa)
|
||||
} else {
|
||||
(keys.sa, keys.oa)
|
||||
};
|
||||
let (Some(source_key), Some(target_key)) = (source_key, target_key) else {
|
||||
return Ok(());
|
||||
};
|
||||
let fact = capture_facet(project, trace.oa, trace.sa, &FacetSelector::Order(which))?;
|
||||
let source: Vec<Id> = serde_json::from_value(
|
||||
scalar::value(&fact, self.direction.source_layer())
|
||||
.ok_or("Source order is unavailable.")?,
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let position = source
|
||||
.iter()
|
||||
.position(|id| *id == source_key)
|
||||
.ok_or("Source member is absent.")?;
|
||||
let map = self.order_reference_map()?;
|
||||
let anchor = |id: &Id| {
|
||||
map.get(id)
|
||||
.and_then(|mapped| before.iter().position(|id| id == mapped))
|
||||
};
|
||||
let left = source[..position].iter().rev().find_map(anchor);
|
||||
let right = source[position + 1..].iter().find_map(anchor);
|
||||
let at = match (left, right) {
|
||||
(Some(a), Some(b)) if a + 1 == b => Some(b),
|
||||
(Some(a), None) if a + 1 == before.len() => Some(a + 1),
|
||||
(None, Some(0)) => Some(0),
|
||||
(None, None) if before.is_empty() => Some(0),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(at) = at {
|
||||
let mut ordered = before;
|
||||
ordered.insert(at, target_key);
|
||||
write(value, which, ordered)
|
||||
} else if form_context {
|
||||
// An uncommitted membership list lets the user author a final order.
|
||||
// The actual request still rejects ambiguous placement until explicitly decided.
|
||||
Ok(())
|
||||
} else {
|
||||
Err("Ordered member insertion has no unique unchanged anchor; select an explicit final Order in Properties.".into())
|
||||
}
|
||||
}
|
||||
}
|
||||
//! Independent membership and complete final order; the core validates every proposal.
|
||||
use super::*;
|
||||
|
||||
pub(super) fn collection(which: OrderedCollection) -> ReferenceCollection {
|
||||
match which {
|
||||
OrderedCollection::Steps => ReferenceCollection::Steps,
|
||||
OrderedCollection::FragmentBranches => ReferenceCollection::FragmentBranches,
|
||||
}
|
||||
}
|
||||
pub(super) fn ordered(which: ReferenceCollection) -> Option<OrderedCollection> {
|
||||
match which {
|
||||
ReferenceCollection::Steps => Some(OrderedCollection::Steps),
|
||||
ReferenceCollection::FragmentBranches => Some(OrderedCollection::FragmentBranches),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub(super) fn read(value: &Value, which: OrderedCollection) -> Result<Vec<Id>, String> {
|
||||
let path = keyed::path(value, keyed::Family::Member(collection(which)))
|
||||
.ok_or("This ordered collection is unavailable on the destination.")?;
|
||||
serde_json::from_value(value.pointer(&path).unwrap().clone()).map_err(|e| e.to_string())
|
||||
}
|
||||
fn write(value: &mut Value, which: OrderedCollection, ids: Vec<Id>) -> Result<(), String> {
|
||||
let path = keyed::path(value, keyed::Family::Member(collection(which)))
|
||||
.ok_or("This ordered collection is unavailable on the destination.")?;
|
||||
*value.pointer_mut(&path).unwrap() = serde_json::to_value(ids).expect("Ordered identities");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl ReviewRow {
|
||||
pub(super) fn ordered_members(
|
||||
&self,
|
||||
project: &Project,
|
||||
which: OrderedCollection,
|
||||
) -> Result<Vec<Id>, String> {
|
||||
read(&self.combine(project, true, true)?, which)
|
||||
}
|
||||
|
||||
pub(super) fn initialize_order(
|
||||
&mut self,
|
||||
project: &Project,
|
||||
which: OrderedCollection,
|
||||
) -> Result<(), String> {
|
||||
if !self.order_drafts.contains_key(&which) {
|
||||
let ids = self.ordered_members(project, which)?;
|
||||
self.order_drafts.insert(which, ids);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn reconcile_order(
|
||||
&mut self,
|
||||
project: &Project,
|
||||
which: OrderedCollection,
|
||||
) -> Result<(), String> {
|
||||
self.require_order_candidate(which)?;
|
||||
let members = self.ordered_members(project, which)?;
|
||||
let draft = self
|
||||
.order_drafts
|
||||
.get_mut(&which)
|
||||
.ok_or("Choose a final order first.")?;
|
||||
// This is an explicit user action. Ordinary refresh never discards an authored order.
|
||||
let mut seen = BTreeSet::new();
|
||||
draft.retain(|id| members.contains(id) && seen.insert(*id));
|
||||
for id in members {
|
||||
if seen.insert(id) {
|
||||
draft.push(id);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn require_order_candidate(&self, which: OrderedCollection) -> Result<(), String> {
|
||||
let facet = FacetSelector::Order(which);
|
||||
if self.selected.as_ref() != Some(&facet)
|
||||
|| self.decisions.get(&facet) != Some(&SemanticDecision::Candidate)
|
||||
{
|
||||
return Err("Select Set final value for this order before changing it.".into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn move_order(
|
||||
&mut self,
|
||||
which: OrderedCollection,
|
||||
index: usize,
|
||||
delta: i32,
|
||||
) -> Result<(), String> {
|
||||
self.require_order_candidate(which)?;
|
||||
let ids = self
|
||||
.order_drafts
|
||||
.get_mut(&which)
|
||||
.ok_or("Choose a final order first.")?;
|
||||
let destination = match delta {
|
||||
-1 => index.checked_sub(1),
|
||||
1 => index.checked_add(1),
|
||||
_ => None,
|
||||
};
|
||||
let destination = destination
|
||||
.filter(|i| index < ids.len() && *i < ids.len())
|
||||
.ok_or("Choose an existing adjacent position in the final order.")?;
|
||||
ids.swap(index, destination);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn project_order(
|
||||
&self,
|
||||
project: &Project,
|
||||
trace: &Trace,
|
||||
value: &mut Value,
|
||||
which: OrderedCollection,
|
||||
decision: SemanticDecision,
|
||||
) -> Result<(), String> {
|
||||
let current = read(value, which)?;
|
||||
let supplied = match decision {
|
||||
SemanticDecision::KeepTarget => return Ok(()),
|
||||
SemanticDecision::Candidate => self
|
||||
.order_drafts
|
||||
.get(&which)
|
||||
.cloned()
|
||||
.ok_or("Choose a final order.")?,
|
||||
SemanticDecision::TakeSource => {
|
||||
let fact =
|
||||
capture_facet(project, trace.oa, trace.sa, &FacetSelector::Order(which))?;
|
||||
let source: Vec<Id> = serde_json::from_value(
|
||||
scalar::value(&fact, self.direction.source_layer())
|
||||
.ok_or("The source has no applicable order.")?,
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let map = self.order_reference_map()?;
|
||||
source
|
||||
.into_iter()
|
||||
.map(|id| {
|
||||
map.get(&id).copied().ok_or_else(|| {
|
||||
format!(
|
||||
"Pair source order member {} explicitly in Reference pairs.",
|
||||
project.named(id)
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
}
|
||||
};
|
||||
let actual: BTreeSet<_> = current.iter().copied().collect();
|
||||
let desired: BTreeSet<_> = supplied.iter().copied().collect();
|
||||
if desired.len() != supplied.len() {
|
||||
return Err(
|
||||
"The final order contains a duplicate identity; reconcile its members explicitly."
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
if desired != actual {
|
||||
if decision == SemanticDecision::TakeSource
|
||||
&& desired.is_subset(&actual)
|
||||
&& current
|
||||
.iter()
|
||||
.filter(|id| desired.contains(id))
|
||||
.copied()
|
||||
.collect::<Vec<_>>()
|
||||
== supplied
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
return Err("Final order must contain every surviving destination member exactly once. Reconcile members, then choose their positions; membership is reviewed separately.".into());
|
||||
}
|
||||
write(value, which, supplied)
|
||||
}
|
||||
|
||||
fn order_reference_map(&self) -> Result<BTreeMap<Id, Id>, String> {
|
||||
let mut map = BTreeMap::new();
|
||||
for (oa, sa) in &self.references {
|
||||
let (a, b) = if self.direction == TransitionDirection::OaToSa {
|
||||
(*oa, *sa)
|
||||
} else {
|
||||
(*sa, *oa)
|
||||
};
|
||||
if map.insert(a, b).is_some_and(|old| old != b) {
|
||||
return Err("Reverse reference correspondence is ambiguous.".into());
|
||||
}
|
||||
}
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
pub(super) fn project_ordered_member(
|
||||
&self,
|
||||
project: &Project,
|
||||
trace: &Trace,
|
||||
value: &mut Value,
|
||||
facet: &FacetSelector,
|
||||
decision: SemanticDecision,
|
||||
form_context: bool,
|
||||
) -> Result<(), String> {
|
||||
let FacetSelector::Member {
|
||||
collection: member_collection,
|
||||
keys,
|
||||
} = facet
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
let which = ordered(*member_collection).expect("Ordered member");
|
||||
let before = read(value, which)?;
|
||||
self.keyed_value(project, trace, value, facet, decision)?;
|
||||
let after = read(value, which)?;
|
||||
let explicit = self
|
||||
.decisions
|
||||
.get(&FacetSelector::Order(which))
|
||||
.is_some_and(|d| *d != SemanticDecision::KeepTarget);
|
||||
if decision != SemanticDecision::TakeSource || explicit || after.len() <= before.len() {
|
||||
return Ok(());
|
||||
}
|
||||
let (source_key, target_key) = if self.direction == TransitionDirection::OaToSa {
|
||||
(keys.oa, keys.sa)
|
||||
} else {
|
||||
(keys.sa, keys.oa)
|
||||
};
|
||||
let (Some(source_key), Some(target_key)) = (source_key, target_key) else {
|
||||
return Ok(());
|
||||
};
|
||||
let fact = capture_facet(project, trace.oa, trace.sa, &FacetSelector::Order(which))?;
|
||||
let source: Vec<Id> = serde_json::from_value(
|
||||
scalar::value(&fact, self.direction.source_layer())
|
||||
.ok_or("Source order is unavailable.")?,
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let position = source
|
||||
.iter()
|
||||
.position(|id| *id == source_key)
|
||||
.ok_or("Source member is absent.")?;
|
||||
let map = self.order_reference_map()?;
|
||||
let anchor = |id: &Id| {
|
||||
map.get(id)
|
||||
.and_then(|mapped| before.iter().position(|id| id == mapped))
|
||||
};
|
||||
let left = source[..position].iter().rev().find_map(anchor);
|
||||
let right = source[position + 1..].iter().find_map(anchor);
|
||||
let at = match (left, right) {
|
||||
(Some(a), Some(b)) if a + 1 == b => Some(b),
|
||||
(Some(a), None) if a + 1 == before.len() => Some(a + 1),
|
||||
(None, Some(0)) => Some(0),
|
||||
(None, None) if before.is_empty() => Some(0),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(at) = at {
|
||||
let mut ordered = before;
|
||||
ordered.insert(at, target_key);
|
||||
write(value, which, ordered)
|
||||
} else if form_context {
|
||||
// An uncommitted membership list lets the user author a final order.
|
||||
// The actual request still rejects ambiguous placement until explicitly decided.
|
||||
Ok(())
|
||||
} else {
|
||||
Err("Ordered member insertion has no unique unchanged anchor; select an explicit final Order in Properties.".into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12837
-12837
File diff suppressed because it is too large
Load Diff
@@ -1,66 +0,0 @@
|
||||
# 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).
|
||||
@@ -1,18 +0,0 @@
|
||||
# Authoring prerelease evidence
|
||||
|
||||
Exact preserved producer, independent reviewer and root native evidence for
|
||||
Windows preview `v0.2.0-preview.authoring.20260911`. `hashes.json` records each
|
||||
copied artifact's original private path, length and SHA256. Git must retain these
|
||||
measured bytes. The executable ZIP and validation notes are release assets.
|
||||
|
||||
The preserved Python files are the original host-specific harnesses. Their
|
||||
relative paths and source/toolchain prerequisites refer to the recorded original
|
||||
`.local` layout; they are not portable standalone tests when run from this archive.
|
||||
To reproduce them, restore that layout in a disposable checkout and use fresh
|
||||
output directories. They never contain Gitea credentials. The GUI launch script
|
||||
only launches the application; native actions were performed using Computer Use.
|
||||
|
||||
See the three `harness/release-authoring-prerelease-*-20260911.md` reports for
|
||||
methods, initial GUI harness failure, passed checks, source provenance and limits.
|
||||
`published-verified.json` records the final release readback; upload records bind
|
||||
the exact downloaded assets. Publication does not complete the wider OA/SA goal.
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"pid": 10800,
|
||||
"exe": "C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\authoring-prerelease-gui-20260911-v2\\extracted\\ArcadiaStudio-0.2.0-preview.authoring.20260911-x86_64-pc-windows-gnullvm\\arcadia-studio.exe",
|
||||
"archive_sha256": "664f30b6217689aca1492eb263b3d15777067d732fa890eeb028f435209dadfa",
|
||||
"files": 559,
|
||||
"path": "C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\authoring-prerelease-gui-20260911-v2\\extracted\\ArcadiaStudio-0.2.0-preview.authoring.20260911-x86_64-pc-windows-gnullvm;C:\\Windows\\System32;C:\\Windows",
|
||||
"local_appdata": "C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\authoring-prerelease-gui-20260911-v2\\profile\\local",
|
||||
"appdata": "C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\authoring-prerelease-gui-20260911-v2\\profile\\roaming"
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
from pathlib import Path
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import zipfile
|
||||
|
||||
root = Path.cwd()
|
||||
name = 'ArcadiaStudio-0.2.0-preview.authoring.20260911-x86_64-pc-windows-gnullvm'
|
||||
archive = root / '.local/authoring-prerelease-20260911/packages' / (name + '.zip')
|
||||
assert hashlib.sha256(archive.read_bytes()).hexdigest() == '664f30b6217689aca1492eb263b3d15777067d732fa890eeb028f435209dadfa'
|
||||
trial = root / '.local/authoring-prerelease-gui-20260911-v2'
|
||||
trial.mkdir(exist_ok=False)
|
||||
extract = trial / 'extracted'
|
||||
extract.mkdir()
|
||||
with zipfile.ZipFile(archive) as z:
|
||||
names = z.namelist()
|
||||
assert len(names) == len(set(n.casefold() for n in names)) == 559
|
||||
assert all(n.startswith(name + '/') and '..' not in Path(n).parts for n in names)
|
||||
z.extractall(extract)
|
||||
package = extract / name
|
||||
for line in (package / 'SHA256SUMS.txt').read_text().splitlines():
|
||||
digest, path = line.split(' ', 1)
|
||||
assert hashlib.sha256((package / path).read_bytes()).hexdigest() == digest, path
|
||||
for folder in ['profile/local', 'profile/roaming']:
|
||||
(trial / folder).mkdir(parents=True)
|
||||
env = os.environ.copy()
|
||||
env['LOCALAPPDATA'] = str(trial / 'profile/local')
|
||||
env['APPDATA'] = str(trial / 'profile/roaming')
|
||||
env['PATH'] = os.pathsep.join([str(package), str(Path(env['SYSTEMROOT']) / 'System32'), env['SYSTEMROOT']])
|
||||
exe = package / 'arcadia-studio.exe'
|
||||
assert hashlib.sha256(exe.read_bytes()).hexdigest() == 'd4d9c2bd937e301ff1009d7cb93a34ee5aa6dfc837ff7e87d468c85371b20c31'
|
||||
with (trial / 'stdout.log').open('wb') as out, (trial / 'stderr.log').open('wb') as err:
|
||||
process = subprocess.Popen([str(exe)], cwd=package, env=env, stdout=out, stderr=err)
|
||||
result = {'pid': process.pid, 'exe': str(exe), 'archive_sha256': hashlib.sha256(archive.read_bytes()).hexdigest(), 'files': len(names), 'path': env['PATH'], 'local_appdata': env['LOCALAPPDATA'], 'appdata': env['APPDATA']}
|
||||
(trial / 'launch.json').write_text(json.dumps(result, indent=2), encoding='utf-8')
|
||||
print(json.dumps(result, indent=2))
|
||||
@@ -1,64 +0,0 @@
|
||||
[
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\authoring-prerelease-gui-20260911-v2\\extracted\\ArcadiaStudio-0.2.0-preview.authoring.20260911-x86_64-pc-windows-gnullvm\\arcadia-studio.exe",
|
||||
"C:\\Windows\\SYSTEM32\\ntdll.dll",
|
||||
"C:\\Windows\\System32\\KERNEL32.DLL",
|
||||
"C:\\Windows\\System32\\KERNELBASE.dll",
|
||||
"C:\\Windows\\SYSTEM32\\apphelp.dll",
|
||||
"C:\\Windows\\System32\\user32.dll",
|
||||
"C:\\Windows\\System32\\win32u.dll",
|
||||
"C:\\Windows\\System32\\GDI32.dll",
|
||||
"C:\\Windows\\System32\\gdi32full.dll",
|
||||
"C:\\Windows\\System32\\msvcp_win.dll",
|
||||
"C:\\Windows\\System32\\ucrtbase.dll",
|
||||
"C:\\Windows\\System32\\ole32.dll",
|
||||
"C:\\Windows\\System32\\combase.dll",
|
||||
"C:\\Windows\\System32\\RPCRT4.dll",
|
||||
"C:\\Windows\\System32\\advapi32.dll",
|
||||
"C:\\Windows\\System32\\msvcrt.dll",
|
||||
"C:\\Windows\\System32\\sechost.dll",
|
||||
"C:\\Windows\\System32\\bcryptprimitives.dll",
|
||||
"C:\\Windows\\System32\\shell32.dll",
|
||||
"C:\\Windows\\System32\\imm32.dll",
|
||||
"C:\\Windows\\System32\\ws2_32.dll",
|
||||
"C:\\Windows\\System32\\oleaut32.dll",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\authoring-prerelease-gui-20260911-v2\\extracted\\ArcadiaStudio-0.2.0-preview.authoring.20260911-x86_64-pc-windows-gnullvm\\libunwind.dll",
|
||||
"C:\\Windows\\SYSTEM32\\bcrypt.dll",
|
||||
"C:\\Windows\\SYSTEM32\\dwmapi.dll",
|
||||
"C:\\Windows\\SYSTEM32\\uxtheme.dll",
|
||||
"C:\\Windows\\SYSTEM32\\opengl32.dll",
|
||||
"C:\\Windows\\SYSTEM32\\GLU32.dll",
|
||||
"C:\\Windows\\SYSTEM32\\CRYPTBASE.DLL",
|
||||
"C:\\Windows\\SYSTEM32\\dxcore.dll",
|
||||
"C:\\Windows\\System32\\MSCTF.dll",
|
||||
"C:\\Windows\\SYSTEM32\\kernel.appcore.dll",
|
||||
"C:\\Windows\\System32\\clbcatq.dll",
|
||||
"C:\\Windows\\system32\\dataexchange.dll",
|
||||
"C:\\Windows\\system32\\twinapi.appcore.dll",
|
||||
"C:\\Windows\\system32\\explorerframe.dll",
|
||||
"C:\\Windows\\System32\\SHCORE.dll",
|
||||
"C:\\Windows\\System32\\SHLWAPI.dll",
|
||||
"C:\\Windows\\System32\\Windows.Storage.dll",
|
||||
"C:\\Windows\\SYSTEM32\\powrprof.dll",
|
||||
"C:\\Windows\\SYSTEM32\\UMPDC.dll",
|
||||
"C:\\Windows\\SYSTEM32\\CFGMGR32.dll",
|
||||
"C:\\Windows\\system32\\dxgi.dll",
|
||||
"C:\\Windows\\SYSTEM32\\windows.staterepositorycore.dll",
|
||||
"C:\\Windows\\SYSTEM32\\directxdatabasehelper.dll",
|
||||
"C:\\Windows\\SYSTEM32\\d3d12.dll",
|
||||
"C:\\Windows\\SYSTEM32\\d3dcompiler_47.dll",
|
||||
"C:\\Windows\\SYSTEM32\\cryptsp.dll",
|
||||
"C:\\Windows\\System32\\DriverStore\\FileRepository\\vm3d.inf_amd64_39d3dfd189710365\\vm3dgl64.dll",
|
||||
"C:\\Windows\\System32\\DriverStore\\FileRepository\\vm3d.inf_amd64_39d3dfd189710365\\vm3dglhelper64.dll",
|
||||
"C:\\Windows\\SYSTEM32\\WINMM.dll",
|
||||
"C:\\Windows\\SYSTEM32\\D3D12Core.dll",
|
||||
"C:\\Windows\\SYSTEM32\\d3d10warp.dll",
|
||||
"C:\\Windows\\SYSTEM32\\D3DSCache.dll",
|
||||
"C:\\Windows\\SYSTEM32\\USERENV.dll",
|
||||
"C:\\Windows\\SYSTEM32\\dxilconv.dll",
|
||||
"C:\\Windows\\SYSTEM32\\dcomp.dll",
|
||||
"C:\\Windows\\SYSTEM32\\Microsoft.Internal.WarpPal.dll",
|
||||
"C:\\Windows\\SYSTEM32\\textinputframework.dll",
|
||||
"C:\\Windows\\SYSTEM32\\uiautomationcore.dll",
|
||||
"C:\\Windows\\SYSTEM32\\OLEACC.dll",
|
||||
"C:\\Windows\\System32\\threadpoolwinrt.dll"
|
||||
]
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"matched_files": 558,
|
||||
"exact_physical_inventory": 559,
|
||||
"result": "PASS after native packaged walkthrough open and Save"
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
[
|
||||
{
|
||||
"path": "producer-inventory.json",
|
||||
"original": ".local/authoring-prerelease-20260911/inventory.json",
|
||||
"bytes": 66465,
|
||||
"sha256": "3c78dea4e93dae186fd24ab826c6680178a2c7849c270a22deae2ff732f1857b"
|
||||
},
|
||||
{
|
||||
"path": "producer-build-evidence.json",
|
||||
"original": ".local/authoring-prerelease-20260911/build-evidence.json",
|
||||
"bytes": 1230,
|
||||
"sha256": "ccc1ccd087f1de9605ba23a4680e1cb6d37ce5ccf0f8db03c03c4797ae32db25"
|
||||
},
|
||||
{
|
||||
"path": "producer-package-result.json",
|
||||
"original": ".local/authoring-prerelease-20260911/package-initial-result.json",
|
||||
"bytes": 216,
|
||||
"sha256": "45f372960931fa34ca75086df5d346eab9127a7ae70b53aed86751e372c24bae"
|
||||
},
|
||||
{
|
||||
"path": "producer-roundtrip.json",
|
||||
"original": ".local/authoring-prerelease-20260911/committed-roundtrip.json",
|
||||
"bytes": 40443,
|
||||
"sha256": "27e4cd2d6130a9ced76560cee130b5d363478ee0da62925e785691c63fadb098"
|
||||
},
|
||||
{
|
||||
"path": "reviewer-artifact-result.json",
|
||||
"original": ".local/authoring-prerelease-review-20260911/artifact-result.json",
|
||||
"bytes": 338784,
|
||||
"sha256": "8b20e9d294338754ddc9b3320b59f55fc19ab979b8da18819fb47a8036db0bba"
|
||||
},
|
||||
{
|
||||
"path": "reviewer-server-result.json",
|
||||
"original": ".local/authoring-prerelease-review-20260911/server-result.json",
|
||||
"bytes": 128779,
|
||||
"sha256": "086bf21e79f3bc7deddfd3278c8ade9b470b11031f7fb278ca0ead3a75b6cfb1"
|
||||
},
|
||||
{
|
||||
"path": "reviewer.py",
|
||||
"original": ".local/authoring-prerelease-review-20260911/review.py",
|
||||
"bytes": 21434,
|
||||
"sha256": "af9eb20946dfcec78bf8959f1d5dc175e82ebb9fabba9f94cefe590fea516b92"
|
||||
},
|
||||
{
|
||||
"path": "gui-launch.json",
|
||||
"original": ".local/authoring-prerelease-gui-20260911-v2/launch.json",
|
||||
"bytes": 827,
|
||||
"sha256": "ade93cb1abea623f1cc63b8124857cad822e4f75e8d1c389f75fd8a9089b2c39"
|
||||
},
|
||||
{
|
||||
"path": "gui-loaded-modules.json",
|
||||
"original": ".local/authoring-prerelease-gui-20260911-v2/loaded-modules.json",
|
||||
"bytes": 3123,
|
||||
"sha256": "ccae3d91310644e171bf35028f1c0758ef330ebb051983e0524b8cd714652b6b"
|
||||
},
|
||||
{
|
||||
"path": "gui-post-save-exact.json",
|
||||
"original": ".local/authoring-prerelease-gui-20260911-v2/post-save-package-exact.json",
|
||||
"bytes": 133,
|
||||
"sha256": "e340289a2c09058c539e63de33a361326b146fc181a8bd1349a88bfc813f2420"
|
||||
},
|
||||
{
|
||||
"path": "upload-binaries-verified.json",
|
||||
"original": ".local/authoring-prerelease-20260911/upload-binaries-verified.json",
|
||||
"bytes": 685,
|
||||
"sha256": "16799aa759e7287f4c50af1068d311f6f916f97e0cfbd7aaddf7432770d281f3"
|
||||
},
|
||||
{
|
||||
"path": "gui-launch.py",
|
||||
"original": ".local/authoring-prerelease-gui-20260911.py",
|
||||
"bytes": 2001,
|
||||
"sha256": "db256498e44cbb67cd84738a1a37f0e9ab0931d130a3b8c830af2c02cff0924c"
|
||||
},
|
||||
{
|
||||
"path": "home.png",
|
||||
"original": ".local/authoring-prerelease-gui-20260911-v2/home.png",
|
||||
"bytes": 97695,
|
||||
"sha256": "e9c24489e949096347b11234d8d2d343716fd5895903c4978d139107a70e3e5a"
|
||||
},
|
||||
{
|
||||
"path": "oa-walkthrough-save.png",
|
||||
"original": ".local/authoring-prerelease-gui-20260911-v2/oa-walkthrough-save.png",
|
||||
"bytes": 131037,
|
||||
"sha256": "61b2cbdd2dddb6fa176000198ae845554147cbc80abe52fa992211157f6297b5"
|
||||
},
|
||||
{
|
||||
"path": "sa-walkthrough.png",
|
||||
"original": ".local/authoring-prerelease-gui-20260911-v2/sa-walkthrough.png",
|
||||
"bytes": 132180,
|
||||
"sha256": "7f3a2bbed9fa69f9d5d844f9a52f81fbc3928244f49d7a9ff1e475c3f2411dbf"
|
||||
},
|
||||
{
|
||||
"path": "state-workspace.png",
|
||||
"original": ".local/authoring-prerelease-gui-20260911-v2/state-workspace.png",
|
||||
"bytes": 135827,
|
||||
"sha256": "79010527e5e1bb8ccf428788a00dea80e6d8253f3d7c249089218e2df9edd6da"
|
||||
},
|
||||
{
|
||||
"path": "offline-guide.png",
|
||||
"original": ".local/authoring-prerelease-gui-20260911-v2/offline-guide.png",
|
||||
"bytes": 185496,
|
||||
"sha256": "58812d2bd0d6e73daa5187f2dd87564189960387ac4bb1e399af4b8d114c05df"
|
||||
},
|
||||
{
|
||||
"path": "published-verified.json",
|
||||
"original": ".local/authoring-prerelease-20260911/published-verified.json",
|
||||
"bytes": 374,
|
||||
"sha256": "668f28c4d8cf753386085271eb181513b0127940f324f423a2b87614ec8cc85a"
|
||||
},
|
||||
{
|
||||
"path": "upload-validation-verified.json",
|
||||
"original": ".local/authoring-prerelease-20260911/upload-validation-verified.json",
|
||||
"bytes": 277,
|
||||
"sha256": "f39859baea544662fc4bdd1fe14dbcb9bdab5639b173754a34b58b853f0d47a7"
|
||||
}
|
||||
]
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 95 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 128 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 181 KiB |
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"source_identity": "Exact-byte reconstructed input snapshot 234e01e27d6935d213097b345d91d369f808af82, derived from release base 2ec72d4241fa8971fa6fe9f626937de9dfd7c7eb by seven documented LF-to-CRLF restorations and preservation attributes. All 253 GUI manifest inputs and 120 preserved server source files matched. This derived commit is not claimed as the original build commit; GUI and server original build records are retained separately.",
|
||||
"profile": "Prebuilt Windows debug/development artifacts: GUI cargo build --locked -p oa-desktop --bin arcadia-studio, September 11 2026; preserved server from September 7 workspace bins build. Not a release-profile or reproducible-build claim.",
|
||||
"compiler": "rustc 1.98.1, commit 48a229ceaefd4985c50990b14116b6d856af0985, x86_64-pc-windows-gnullvm, LLVM 22.1.8. Current rustc -vV, all 58 prior-notice target-library hashes and both embedded compiler paths matched; original recorded build evidence retained.",
|
||||
"target": "x86_64-pc-windows-gnullvm",
|
||||
"binary_sha256": {
|
||||
"arcadia-studio.exe": "d4d9c2bd937e301ff1009d7cb93a34ee5aa6dfc837ff7e87d468c85371b20c31",
|
||||
"arcadia-server.exe": "efeeb89c0d2e0f93df4472cb3e9f48b2b722a37ae1043c90a5a56f2e845fe665"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"started": "2026-09-11T12:17:30.7730570Z",
|
||||
"source_commit": "234e01e27d6935d213097b345d91d369f808af82",
|
||||
"success": true,
|
||||
"finished": "2026-09-11T12:21:23.0302800Z",
|
||||
"elapsed_seconds": 232.2812442
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"id": 1,
|
||||
"tag": "v0.2.0-preview.authoring.20260911",
|
||||
"url": "http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/releases/tag/v0.2.0-preview.authoring.20260911",
|
||||
"draft": false,
|
||||
"prerelease": true,
|
||||
"asset_count": 3,
|
||||
"notes_sha256": "e48399db78b18c6bd779412f7aeccf13fe78bf8c9bac22a4c418755acfab175c",
|
||||
"published_at": "2026-09-11T12:40:51Z"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,335 +0,0 @@
|
||||
"""Independent frozen archive and packaged-server checks; never runs Cargo or GUI."""
|
||||
from pathlib import Path, PurePosixPath
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import stat
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
import time
|
||||
import tomllib
|
||||
import traceback
|
||||
import urllib.request
|
||||
import uuid
|
||||
import zipfile
|
||||
|
||||
OUT = Path(__file__).resolve().parent
|
||||
ROOT = OUT.parents[1]
|
||||
PREP = ROOT / '.local/authoring-prerelease-20260911'
|
||||
NAME = 'ArcadiaStudio-0.2.0-preview.authoring.20260911-x86_64-pc-windows-gnullvm'
|
||||
ZIP = PREP / 'packages' / (NAME + '.zip')
|
||||
EXTRACT = OUT / 'extracted'
|
||||
PACKAGE = EXTRACT / NAME
|
||||
SOURCE = PREP / 'source'
|
||||
SOURCE_COMMIT = '234e01e27d6935d213097b345d91d369f808af82'
|
||||
BASE_COMMIT = '2ec72d4241fa8971fa6fe9f626937de9dfd7c7eb'
|
||||
EXPECTED_ZIP = '664f30b6217689aca1492eb263b3d15777067d732fa890eeb028f435209dadfa'
|
||||
BINARIES = {
|
||||
'arcadia-studio.exe': 'd4d9c2bd937e301ff1009d7cb93a34ee5aa6dfc837ff7e87d468c85371b20c31',
|
||||
'arcadia-server.exe': 'efeeb89c0d2e0f93df4472cb3e9f48b2b722a37ae1043c90a5a56f2e845fe665',
|
||||
'libunwind.dll': 'f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4',
|
||||
}
|
||||
RESULT = {'checks': [], 'scope': sys.argv[1], 'started_utc': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())}
|
||||
|
||||
|
||||
def check(condition, label):
|
||||
if not condition:
|
||||
raise AssertionError(label)
|
||||
RESULT['checks'].append(label)
|
||||
|
||||
|
||||
def sha(data):
|
||||
return hashlib.sha256(data).hexdigest()
|
||||
|
||||
|
||||
def read_json(path):
|
||||
return json.loads(path.read_text(encoding='utf-8-sig'))
|
||||
|
||||
|
||||
def write_json(path, value):
|
||||
with path.open('x', encoding='utf-8', newline='\n') as f:
|
||||
json.dump(value, f, indent=2, ensure_ascii=False)
|
||||
f.write('\n')
|
||||
|
||||
|
||||
def file_map(directory):
|
||||
result = {}
|
||||
for p in directory.rglob('*'):
|
||||
check(not p.is_symlink() and not (getattr(p.lstat(), 'st_file_attributes', 0) & 0x400), 'ordinary path: ' + p.relative_to(directory).as_posix())
|
||||
if p.is_file():
|
||||
result[p.relative_to(directory).as_posix()] = sha(p.read_bytes())
|
||||
return result
|
||||
|
||||
|
||||
def safe_name(name):
|
||||
p = PurePosixPath(name)
|
||||
return not p.is_absolute() and p.as_posix() == name and '\\' not in name and ':' not in name and all(c not in ('', '.', '..') and not c.endswith(('.', ' ')) and not re.match(r'(?i)^(con|nul|prn|aux|com[1-9]|lpt[1-9])(\.|$)', c) for c in p.parts)
|
||||
|
||||
|
||||
def git_blobs(ref, paths):
|
||||
raw = subprocess.run(['git', 'cat-file', '--batch'], input=''.join(ref + ':' + p + '\n' for p in paths).encode(), cwd=ROOT, capture_output=True, check=True).stdout
|
||||
result, cursor = {}, 0
|
||||
for p in paths:
|
||||
end = raw.index(b'\n', cursor)
|
||||
header = raw[cursor:end].split()
|
||||
check(len(header) == 3 and header[1] == b'blob', 'Git blob exists: ' + ref[:7] + ':' + p)
|
||||
size = int(header[2])
|
||||
result[p] = raw[end + 1:end + 1 + size]
|
||||
cursor = end + 2 + size
|
||||
check(cursor == len(raw), 'Git batch framing complete: ' + ref[:7])
|
||||
return result
|
||||
|
||||
|
||||
def artifact():
|
||||
check(not EXTRACT.exists(), 'clean extraction destination absent')
|
||||
check(ZIP.stat().st_size == 32618262 and sha(ZIP.read_bytes()) == EXPECTED_ZIP, 'frozen ZIP size and SHA256')
|
||||
with zipfile.ZipFile(ZIP) as archive:
|
||||
infos = archive.infolist()
|
||||
names = [i.filename for i in infos]
|
||||
check(len(infos) == 559, '559 ZIP members')
|
||||
check(len({n.casefold() for n in names}) == len(names), 'no duplicate or case-colliding ZIP names')
|
||||
check(all(safe_name(n) and n.startswith(NAME + '/') for n in names), 'one safe ZIP root without traversal or Windows special paths')
|
||||
check(all(not i.is_dir() and stat.S_IFMT(i.external_attr >> 16) in (0, stat.S_IFREG) for i in infos), 'ordinary file-only ZIP entries')
|
||||
check(all(i.date_time == (2000, 1, 1, 0, 0, 0) and i.flag_bits & 1 == 0 for i in infos), 'fixed timestamps and no encrypted entries')
|
||||
check(archive.testzip() is None, 'all ZIP CRCs')
|
||||
EXTRACT.mkdir()
|
||||
archive.extractall(EXTRACT)
|
||||
archive_hashes = {i.filename[len(NAME) + 1:]: sha(archive.read(i)) for i in infos}
|
||||
actual = file_map(PACKAGE)
|
||||
check(actual == archive_hashes, 'complete extracted file set and bytes equal ZIP')
|
||||
sums = {}
|
||||
for line in (PACKAGE / 'SHA256SUMS.txt').read_text().splitlines():
|
||||
match = re.fullmatch(r'([0-9a-f]{64}) (.+)', line)
|
||||
check(match is not None and safe_name(match[2]) and match[2] not in sums, 'well-formed unique checksum row')
|
||||
sums[match[2]] = match[1]
|
||||
check(sums == {p: h for p, h in actual.items() if p != 'SHA256SUMS.txt'}, 'checksum inventory covers every other file exactly')
|
||||
package = read_json(PACKAGE / 'package.json')
|
||||
inputs = package['input_sha256']
|
||||
generated = {'SHA256SUMS.txt', 'START-HERE.txt', 'THIRD-PARTY-NOTICES.txt', 'dependency-notices.json', 'documentation-check.json', 'package.json'}
|
||||
check(set(actual) - set(inputs) == generated and all(actual[p] == h.lower() for p, h in inputs.items()), '553 package input hashes plus exactly six generated files')
|
||||
check(package['version'] == '0.2.0-preview.authoring.20260911' and package['target'] == 'x86_64-pc-windows-gnullvm', 'version and target')
|
||||
check(all(actual[p] == h for p, h in BINARIES.items()), 'exact GUI, server and runtime hashes')
|
||||
for p in BINARIES:
|
||||
data = (PACKAGE / p).read_bytes()
|
||||
offset = struct.unpack_from('<I', data, 0x3c)[0]
|
||||
check(data[:2] == b'MZ' and data[offset:offset + 4] == b'PE\0\0' and struct.unpack_from('<H', data, offset + 4)[0] == 0x8664, 'ordinary PE x86_64: ' + p)
|
||||
check({p for p in actual if '/' not in p} == {'arcadia-server.exe', 'arcadia-studio.exe', 'libunwind.dll', 'LICENSE', 'README.md'} | generated, 'only declared portable top-level files')
|
||||
check({p.split('/')[0] for p in actual if '/' in p} == {'deployment', 'docs', 'examples', 'licenses'}, 'no harness, private profile, source checkout or credential-directory payload')
|
||||
for p in actual:
|
||||
if p.startswith(('docs/', 'examples/', 'deployment/')) or p == 'LICENSE':
|
||||
check((SOURCE / p).is_file() and sha((SOURCE / p).read_bytes()) == actual[p], 'public source document/example bytes: ' + p)
|
||||
RESULT['archive'] = {'path': str(ZIP), 'sha256': EXPECTED_ZIP, 'bytes': ZIP.stat().st_size, 'file_count': len(actual), 'files': actual}
|
||||
RESULT['package_claims'] = {k: package[k] for k in ('version', 'target', 'acceptance', 'build', 'build_evidence')}
|
||||
provenance()
|
||||
notices(actual)
|
||||
|
||||
|
||||
def provenance():
|
||||
gui_path = ROOT / '.local/behavior-creation-20260911/gui-verified/manifest.json'
|
||||
gui = read_json(gui_path)
|
||||
expected = {i['path']: i['sha256'].lower() for i in gui['inputs']}
|
||||
check(len(expected) == 253, 'original GUI manifest has 253 unique inputs')
|
||||
derived = git_blobs(SOURCE_COMMIT, list(expected))
|
||||
base = git_blobs(BASE_COMMIT, list(expected))
|
||||
differences = []
|
||||
for p, h in expected.items():
|
||||
check(sha(derived[p]) == h == sha((SOURCE / p).read_bytes()), 'original GUI input equals derived Git blob and checkout: ' + p)
|
||||
if sha(base[p]) != h:
|
||||
check(sha(base[p].replace(b'\r\n', b'\n').replace(b'\n', b'\r\n')) == h, 'exact LF-to-CRLF-only restoration: ' + p)
|
||||
differences.append(p)
|
||||
check(set(differences) == {'docs/openapi.json'} | {'crates/oa-desktop/src/semantic_review/' + p + '.rs' for p in ('artifacts_view', 'attachments_view', 'keyed_rows', 'keyed_tests', 'keyed', 'ordered')}, 'exact seven documented CRLF restorations')
|
||||
delta = subprocess.run(['git', 'diff', '--name-only', '--ignore-space-at-eol', BASE_COMMIT, SOURCE_COMMIT], cwd=ROOT, check=True, capture_output=True).stdout.decode().splitlines()
|
||||
check(delta == ['.gitattributes'], 'derived commit changes only preservation attributes beyond EOL bytes')
|
||||
origin = read_json(ROOT / 'harness/experiments/textual-authoring-20260911-local/provenance.json')
|
||||
tar_path = ROOT / '.local/linux-qa/runtime/source7-20260907T082753190288Z.tar.gz'
|
||||
check(sha(tar_path.read_bytes()) == 'cbb371a93a562e1e641d7629fe351e016d63e66fc9112499b3fed6d5cd171f78', 'original server source tar identity')
|
||||
server_expected = {i['path']: i['snapshot_sha256'].lower() for i in origin['source_comparisons']}
|
||||
check(len(server_expected) == 120, '120 independently recorded server source files')
|
||||
server_base = git_blobs(BASE_COMMIT, list(server_expected))
|
||||
tar_manifest = read_json(tar_path.with_suffix('').with_suffix('.tar.manifest.json'))
|
||||
with tarfile.open(tar_path) as tar:
|
||||
members = {m.name.removeprefix('./'): m for m in tar.getmembers() if m.isfile()}
|
||||
for p, h in server_expected.items():
|
||||
check(sha(tar.extractfile(members[p]).read()) == h == sha(server_base[p]) == sha((SOURCE / p).read_bytes()), 'server recorded tar equals base Git and staged input: ' + p)
|
||||
shared = [p for p in members if p in ('Cargo.toml', 'Cargo.lock', 'docs/openapi.json') or p.startswith('build/')]
|
||||
for p in shared:
|
||||
check(sha(tar.extractfile(members[p]).read()) == expected[p] == tar_manifest['files'][p].lower(), 'original server/GUI shared manifest or build input: ' + p)
|
||||
evidence = read_json(PACKAGE / 'package.json')['build_evidence']['evidence']
|
||||
check(evidence['binary_sha256'] == {k: v for k, v in BINARIES.items() if k.endswith('.exe')}, 'packaged build evidence binds both exact executables')
|
||||
check(SOURCE_COMMIT in evidence['source_identity'] and BASE_COMMIT in evidence['source_identity'], 'evidence identifies auxiliary and base sources distinctly')
|
||||
check('not claimed as the original build commit' in evidence['source_identity'] and 'Not a release-profile or reproducible-build claim' in evidence['profile'], 'packaged provenance retains explicit limitations')
|
||||
RESULT['provenance'] = {'gui_manifest_sha256': sha(gui_path.read_bytes()), 'gui_build_record': gui['build'], 'historical_gui_head': gui['head'], 'gui_inputs': 253, 'source_commit': SOURCE_COMMIT, 'base_commit': BASE_COMMIT, 'eol_restorations': differences, 'server_source_files': 120, 'shared_input_files': shared, 'tar_sha256': sha(tar_path.read_bytes())}
|
||||
|
||||
|
||||
def notices(actual):
|
||||
notice = read_json(PACKAGE / 'dependency-notices.json')
|
||||
check(notice['root_packages'] == ['oa-desktop', 'oa-server'] and len(notice['packages']) == 266, '266 notice packages and exact desktop/server roots')
|
||||
check(notice['cargo_lock_sha256'].lower() == sha((SOURCE / 'Cargo.lock').read_bytes()), 'notice lock equals exact historical source lock')
|
||||
for p, h in notice['workspace_build_inputs_sha256'].items():
|
||||
check(sha((SOURCE / p).read_bytes()) == h, 'notice common build input: ' + p)
|
||||
lock = tomllib.loads((SOURCE / 'Cargo.lock').read_text())
|
||||
locked = {(p['name'], p['version'], p.get('source')): p for p in lock['package']}
|
||||
referenced = set()
|
||||
def refs(items):
|
||||
for i in items:
|
||||
p = i['path']
|
||||
check(safe_name(p) and p.startswith('licenses/') and actual.get(p) == i['sha256'].lower(), 'bundled exact notice reference: ' + p)
|
||||
referenced.add(p)
|
||||
fonts = []
|
||||
for p in notice['packages']:
|
||||
key = (p['name'], p['version'], p['source'])
|
||||
check(key in locked and p['registry_checksum'] == locked[key].get('checksum'), 'notice identity/checksum occurs in historical lock: ' + p['name'] + '@' + p['version'])
|
||||
check(len(p['notices']) > 0, 'package has notice text: ' + p['name'])
|
||||
refs(p['notices'])
|
||||
for f in p['embedded_fonts']:
|
||||
check(len(f['notices']) > 0, 'embedded font notice present: ' + p['name'] + '/' + f['path'])
|
||||
refs(f['notices'])
|
||||
fonts.append({'package': p['name'], 'path': f['path'], 'sha256': f['sha256'], 'license': f['license']})
|
||||
if p['source'] is None:
|
||||
directory = SOURCE / 'crates' / p['name']
|
||||
files = {f.relative_to(directory).as_posix(): sha(f.read_bytes()) for f in directory.rglob('*') if f.is_file() and 'target' not in f.relative_to(directory).parts and f.name != '.cargo-ok'}
|
||||
check(files == p['local_source_sha256'], 'complete ordinary local-crate inventory: ' + p['name'])
|
||||
refs(notice['rust']['notices'])
|
||||
refs(notice['runtime']['notices'])
|
||||
check(notice['runtime']['sha256'] == BINARIES['libunwind.dll'], 'runtime notice is bound to shipped DLL')
|
||||
library_dir = ROOT / '.local/toolchain/rustup/toolchains/stable-x86_64-pc-windows-gnullvm/lib/rustlib/x86_64-pc-windows-gnullvm/lib'
|
||||
check(len(notice['rust']['target_libraries_sha256']) == 58, '58 compiler target libraries recorded')
|
||||
for name, h in notice['rust']['target_libraries_sha256'].items():
|
||||
check(sha((library_dir / name).read_bytes()) == h, 'retained compiler target-library bytes: ' + name)
|
||||
RESULT['notices'] = {'packages': len(notice['packages']), 'roots': notice['root_packages'], 'lock_sha256': notice['cargo_lock_sha256'], 'notice_reference_files': len(referenced), 'additional_license_files': sorted({p for p in actual if p.startswith('licenses/')} - referenced), 'embedded_fonts': fonts, 'rust_version': notice['rust']['version'], 'runtime_provenance': notice['runtime']['provenance'], 'scope': notice['scope']}
|
||||
|
||||
|
||||
def server():
|
||||
prior = read_json(OUT / 'artifact-result.json')
|
||||
check(prior['status'] == 'PASS' and file_map(PACKAGE) == prior['archive']['files'], 'previously verified extracted package is unchanged')
|
||||
runtime = OUT / 'server-run'
|
||||
runtime.mkdir()
|
||||
data_dir = runtime / 'data'
|
||||
temp = runtime / 'temp'
|
||||
temp.mkdir()
|
||||
windows = Path(os.environ['SystemRoot'])
|
||||
env = {'SystemRoot': str(windows), 'WINDIR': str(windows), 'PATH': str(windows / 'System32') + ';' + str(windows), 'TEMP': str(temp), 'TMP': str(temp)}
|
||||
exe = PACKAGE / 'arcadia-server.exe'
|
||||
RESULT['environment'] = env
|
||||
RESULT['processes'] = []
|
||||
for arg in ('--version', '--help'):
|
||||
output = subprocess.run([str(exe), arg], cwd=PACKAGE, env=env, capture_output=True, timeout=15, creationflags=subprocess.CREATE_NO_WINDOW)
|
||||
(runtime / (arg[2:] + '.stdout')).write_bytes(output.stdout)
|
||||
(runtime / (arg[2:] + '.stderr')).write_bytes(output.stderr)
|
||||
check(output.returncode == 0 and not output.stderr, arg + ' succeeds under clean runtime PATH')
|
||||
if arg == '--version':
|
||||
check(output.stdout.decode().strip() == 'arcadia-server 0.2.0', 'server reports underlying application version 0.2.0')
|
||||
check(not data_dir.exists(), 'help/version do not create explicit test data directory')
|
||||
opener = urllib.request.build_opener(urllib.request.ProxyHandler({}))
|
||||
request_number = 0
|
||||
def request(port, method, path, payload=None):
|
||||
nonlocal request_number
|
||||
request_number += 1
|
||||
body = None if payload is None else json.dumps(payload, ensure_ascii=False).encode()
|
||||
req = urllib.request.Request(f'http://127.0.0.1:{port}' + path, data=body, method=method, headers={} if body is None else {'Content-Type': 'application/json'})
|
||||
with opener.open(req, timeout=10) as response:
|
||||
raw = response.read()
|
||||
meta = {'method': method, 'path': path, 'status': response.status, 'headers': dict(response.headers), 'body_sha256': sha(raw)}
|
||||
prefix = runtime / f'http-{request_number:02d}'
|
||||
prefix.with_suffix('.body').write_bytes(raw)
|
||||
write_json(prefix.with_suffix('.json'), meta)
|
||||
return meta, raw
|
||||
def start(index):
|
||||
with socket.socket() as s:
|
||||
s.bind(('127.0.0.1', 0))
|
||||
port = s.getsockname()[1]
|
||||
out = (runtime / f'server-{index}.stdout').open('xb')
|
||||
err = (runtime / f'server-{index}.stderr').open('xb')
|
||||
args = [str(exe), '--bind', f'127.0.0.1:{port}', '--data-dir', str(data_dir)]
|
||||
process = subprocess.Popen(args, cwd=PACKAGE, env=env, stdout=out, stderr=err, creationflags=subprocess.CREATE_NO_WINDOW)
|
||||
out.close()
|
||||
err.close()
|
||||
RESULT['processes'].append({'pid': process.pid, 'port': port, 'arguments': args})
|
||||
deadline = time.monotonic() + 15
|
||||
while True:
|
||||
if process.poll() is not None:
|
||||
raise AssertionError('packaged server exited before listening')
|
||||
try:
|
||||
with socket.create_connection(('127.0.0.1', port), timeout=.2):
|
||||
break
|
||||
except OSError:
|
||||
if time.monotonic() > deadline:
|
||||
process.terminate()
|
||||
process.wait(timeout=5)
|
||||
raise AssertionError('packaged server did not listen within 15s')
|
||||
time.sleep(.05)
|
||||
return process, port
|
||||
def stop(process):
|
||||
process.terminate()
|
||||
code = process.wait(timeout=10)
|
||||
RESULT['processes'][-1]['exit_after_explicit_terminate'] = code
|
||||
process = None
|
||||
try:
|
||||
process, port = start(1)
|
||||
for path in ('/health', '/health/ready'):
|
||||
meta, raw = request(port, 'GET', path)
|
||||
check(meta['status'] == 200 and json.loads(raw) == {'status': 'ok'}, path + ' contract')
|
||||
meta, raw = request(port, 'GET', '/api/v1/openapi.json')
|
||||
check(meta['status'] == 200 and raw == (PACKAGE / 'docs/openapi.json').read_bytes(), 'served OpenAPI equals complete packaged source bytes')
|
||||
name = 'Independent package persistence — 救助 20260911'
|
||||
meta, raw = request(port, 'POST', '/api/v1/projects', {'name': name})
|
||||
created = json.loads(raw)
|
||||
check(meta['status'] == 201, 'one create returns 201')
|
||||
resource = str(uuid.UUID(created['resource_id']))
|
||||
model_id = str(uuid.UUID(created['model_id']))
|
||||
expected_summary = {'resource_id': resource, 'revision': 1, 'model_id': model_id, 'schema_version': 7, 'name': name, 'elements': 0, 'relations': 0, 'migration': None}
|
||||
check(created == expected_summary and resource != model_id, 'complete create summary with distinct fresh resource/model IDs')
|
||||
etag = f'"p-{resource}-r1"'
|
||||
headers = {k.lower(): v for k, v in meta['headers'].items()}
|
||||
path = '/api/v1/projects/' + resource
|
||||
check(headers.get('etag') == etag and headers.get('location') == path, 'create ETag and resource Location')
|
||||
meta, raw = request(port, 'GET', path)
|
||||
stored = json.loads(raw)
|
||||
model = {'schema_version': 7, 'id': model_id, 'alias': 'mission', 'name': name, 'description': '', 'elements': {}, 'relations': {}, 'diagrams': {}, 'artifact_links': {}, 'traces': {}}
|
||||
source = f'arcadia 7;\nproject mission {json.dumps(name, ensure_ascii=False)} id "{model_id}" {{\n description "";\n}}\n'
|
||||
expected = {'resource_id': resource, 'revision': 1, 'model': model, 'source_project': {'entry': 'project.arcadia', 'files': {'project.arcadia': source}}}
|
||||
check(meta['status'] == 200 and stored == expected, 'GET equals independently predicted complete Project and SourceBundle')
|
||||
check({k.lower(): v for k, v in meta['headers'].items()}.get('etag') == etag, 'GET ETag equals created revision')
|
||||
disk = data_dir / (resource + '.json')
|
||||
disk_bytes = disk.read_bytes()
|
||||
check(json.loads(disk_bytes) == expected, 'durable disk envelope equals complete expected GET')
|
||||
check({p.name for p in data_dir.iterdir()} == {'.arcadia.lock', disk.name}, 'private store contains only one project and lock')
|
||||
stop(process)
|
||||
process = None
|
||||
process, port = start(2)
|
||||
meta, raw = request(port, 'GET', '/health/ready')
|
||||
check(meta['status'] == 200 and json.loads(raw) == {'status': 'ok'}, 'restarted packaged server ready')
|
||||
meta, raw = request(port, 'GET', path)
|
||||
check(meta['status'] == 200 and json.loads(raw) == expected, 'restart retains exact complete Project and SourceBundle')
|
||||
check({k.lower(): v for k, v in meta['headers'].items()}.get('etag') == etag and disk.read_bytes() == disk_bytes, 'restart retains revision, ETag and every durable envelope byte')
|
||||
RESULT['persistence'] = {'resource_id': resource, 'model_id': model_id, 'revision': 1, 'etag': etag, 'disk_sha256': sha(disk_bytes), 'expected': expected, 'stop_kind': 'explicit process termination after acknowledged durable create; not a graceful-drain claim'}
|
||||
finally:
|
||||
if process is not None and process.poll() is None:
|
||||
stop(process)
|
||||
check(file_map(PACKAGE) == prior['archive']['files'], 'server execution leaves complete extracted package unchanged')
|
||||
check(sha(ZIP.read_bytes()) == EXPECTED_ZIP, 'frozen ZIP unchanged after independent runtime checks')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
result_path = OUT / (sys.argv[1] + '-result.json')
|
||||
if result_path.exists():
|
||||
raise SystemExit('Refusing to overwrite prior evidence: ' + str(result_path))
|
||||
try:
|
||||
{'artifact': artifact, 'server': server}[sys.argv[1]]()
|
||||
RESULT['status'] = 'PASS'
|
||||
except Exception as error:
|
||||
RESULT['status'] = 'FAIL'
|
||||
RESULT['error'] = repr(error)
|
||||
RESULT['traceback'] = traceback.format_exc()
|
||||
RESULT['finished_utc'] = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
|
||||
RESULT['script_sha256'] = sha(Path(__file__).read_bytes())
|
||||
write_json(result_path, RESULT)
|
||||
print(json.dumps({k: RESULT[k] for k in ('scope', 'status', 'started_utc', 'finished_utc')}, indent=2))
|
||||
print('checks:', len(RESULT['checks']))
|
||||
if RESULT['status'] == 'FAIL':
|
||||
print(RESULT['traceback'])
|
||||
sys.exit(1)
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 129 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 133 KiB |
@@ -1,18 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "ArcadiaStudio-0.2.0-preview.authoring.20260911-x86_64-pc-windows-gnullvm.zip",
|
||||
"bytes": 32618262,
|
||||
"sha256": "664f30b6217689aca1492eb263b3d15777067d732fa890eeb028f435209dadfa",
|
||||
"url": "http://217.177.34.108:3000/attachments/48a283b7-5391-4677-9427-02a772e5e7e4",
|
||||
"readback": "PASS"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "ArcadiaStudio-0.2.0-preview.authoring.20260911-x86_64-pc-windows-gnullvm.zip.sha256",
|
||||
"bytes": 143,
|
||||
"sha256": "ea553a85f35d2d48f229dd4cd00ac0c3d4a6e9dafa58273004b2c49d1279f856",
|
||||
"url": "http://217.177.34.108:3000/attachments/08164a39-9292-4b2d-9856-c6ec41d9b8e2",
|
||||
"readback": "PASS"
|
||||
}
|
||||
]
|
||||
@@ -1,10 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": 3,
|
||||
"name": "VALIDATION.md",
|
||||
"bytes": 29308,
|
||||
"sha256": "c2a70be735c780d3e468d5f12643fb00be75673434211366739926536bbbe9b9",
|
||||
"url": "http://217.177.34.108:3000/attachments/9ff53361-eedf-46a0-aa4e-34f11bb0f302",
|
||||
"readback": "PASS"
|
||||
}
|
||||
]
|
||||
@@ -1,15 +0,0 @@
|
||||
# Standalone CLI package evidence
|
||||
|
||||
Exact frozen author and independent reviewer evidence for the Windows
|
||||
`ArcadiaCLI-0.2.0-preview.authoring.20260911` artifact. `hashes.json` records
|
||||
original private paths, lengths and hashes; preserve these bytes through Git.
|
||||
|
||||
The original host-specific reviewer script expects its recorded `.local` layout,
|
||||
source snapshot and fresh disposable outputs. Restore that layout to reproduce
|
||||
it; running the archived file in this directory is not a portable test command.
|
||||
The production packaging tests remain under `scripts/`.
|
||||
|
||||
The author report records 15 inert checks, 13 Python tests, default-preserving
|
||||
notice selection and strict package assembly. Independent review verifies source
|
||||
equivalence/defaults, all 114 archive files and 156 assertions across 10 clean
|
||||
runtime invocations. Neither completed the wider OA/SA product or Linux acceptance.
|
||||
@@ -1,5 +0,0 @@
|
||||
.............
|
||||
----------------------------------------------------------------------
|
||||
Ran 13 tests in 1.252s
|
||||
|
||||
OK
|
||||
@@ -1,454 +0,0 @@
|
||||
{
|
||||
"assembly_commit": "a0a48e82bd43be181caca1fe2c1514c21f2bc405",
|
||||
"semantic_commit": "01b76a1e89e93b7992f39e63560aa8b2ca2f69e0",
|
||||
"packaging_files": [
|
||||
{
|
||||
"path": "scripts/package-cli-windows.ps1",
|
||||
"sha256": "b6ec59bb55ff1e5624c782f492ce1d169131e708f257dcca195957edf7341c1a"
|
||||
},
|
||||
{
|
||||
"path": "scripts/package-common.ps1",
|
||||
"sha256": "d859453a8230b4f305d822c8742b371a71e35df698109a5d55c948f6dfaf0640"
|
||||
},
|
||||
{
|
||||
"path": "scripts/package-windows.ps1",
|
||||
"sha256": "28997ff78565619ed9c9b185f66dc922808f1f0be17f522b430c6c415448663d"
|
||||
},
|
||||
{
|
||||
"path": "scripts/windows-notices.ps1",
|
||||
"sha256": "a5328c05579e1b9ca09142ee99e87c12994d12764f7c69448e392883d718ef01"
|
||||
},
|
||||
{
|
||||
"path": "scripts/package-artifacts.py",
|
||||
"sha256": "24515fdbf4eebfb47b82383c6b3ccf290261cbd919f7f46a586b6488af769c8d"
|
||||
},
|
||||
{
|
||||
"path": "scripts/package-cli-readme.md",
|
||||
"sha256": "ea7bc971993e02751f24bc3cb313503a5f5fefd788540974e114398115c7c8e2"
|
||||
},
|
||||
{
|
||||
"path": "scripts/test-package-cli-windows.ps1",
|
||||
"sha256": "2c18f91149ede5c7b677a165bd0d8b31f3c4691642cc10dd1007a7a77bde912a"
|
||||
},
|
||||
{
|
||||
"path": "scripts/test-package-artifacts.py",
|
||||
"sha256": "881718181a36fa5f7abfcd2e746bc1339446f9aeed6b2c039b8d2ce230ec202d"
|
||||
},
|
||||
{
|
||||
"path": "scripts/test-windows-notices.ps1",
|
||||
"sha256": "00a6ad1ed28fdb9044ceb7cc0714ceb7af095db2fb18f4828a053aa872a3147d"
|
||||
}
|
||||
],
|
||||
"exact_cli_inputs": [
|
||||
{
|
||||
"path": "Cargo.toml",
|
||||
"sha256": "369f5b4b2b60705a94623c8c0b68f8e0b048f263280ce07ae0629ad353797d62"
|
||||
},
|
||||
{
|
||||
"path": "Cargo.lock",
|
||||
"sha256": "2b960242c33eb839ff008d01731f6d5cdb4e047ca8cd6e13151cbf9c09913510"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-cli/Cargo.toml",
|
||||
"sha256": "49c567f1af74a826683b4d96845feb3806fd9bcb7dc2767e16b5509d4e5e1cde"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-cli/src/lib.rs",
|
||||
"sha256": "97bebab63b4a611a1d36ad0c20fe452cbe1985fe3a6fbd39c954603da11410fc"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-cli/src/main.rs",
|
||||
"sha256": "de964f9b6c59c0f31c080afbd3b31d442b07b1b44f6a187a248d3409c45d35fb"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-cli/tests/source_cli.rs",
|
||||
"sha256": "a3a1b856c7a9fd3d80bc8074150a24fbb0ef0e9ddf736c7cea036725fc3dc2b8"
|
||||
}
|
||||
],
|
||||
"unchanged_core_shared_build_inputs": [
|
||||
{
|
||||
"path": "build/windows_manifest.rs",
|
||||
"sha256": "168da2c42ecccfebc11ee3e1b455e653a68fe9c6d30a9842a8b9e7441d086c80"
|
||||
},
|
||||
{
|
||||
"path": "build/windows.manifest",
|
||||
"sha256": "9c19c5a02f858ece84c44839ad2b145d9c1d90442da0b15c87fd045f975974eb"
|
||||
},
|
||||
{
|
||||
"path": "build/windows.rc",
|
||||
"sha256": "a27385edcae0bcf96f55361056cb7be5f36d76fcddcaefd20647c91959563e1e"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/build.rs",
|
||||
"sha256": "ab1be6ae1fd8bf3ebaeef6348347a9d5e666bde693126f199e041659e0b094a9"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/Cargo.toml",
|
||||
"sha256": "64824ec447be777841a972bf06c894ef2624a37e4725583bcfe0389deb9f00a3"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/examples/generate_demo.rs",
|
||||
"sha256": "e26f432c95331aeca50985dbb3ad2c558affd43023b585811f0f7bfaa59f6661"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/examples/generate_release1.rs",
|
||||
"sha256": "1d5f11165d74893e4bc18dbf3a2f2111f4026bd89fd06ee7f0bae69e45c4d4b5"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/examples/release_dense_views.rs",
|
||||
"sha256": "a83158488ef9b7394f136c52b1496719be935c4e5bdfe457cab5c9894f5f9412"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/examples/routing_workshop.rs",
|
||||
"sha256": "571307b18b6d4fa04700c5a4d4b0c2154c419e93a21292b2028e7b6f46a2faa7"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/data.rs",
|
||||
"sha256": "7f9c7c7dd8353cb5a38597b92cde6d3e890203d392473187eb387d41ff37414a"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/demo.rs",
|
||||
"sha256": "ad62123f96c20ff023cafc1a9849b0fa06bc62edd1fb9701799e4599b458600c"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/language_nesting_tests.rs",
|
||||
"sha256": "bd061611e8cb6f28ab203c73e544ac98bf275eb464cd314ad5fbd25f773df33b"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/language.rs",
|
||||
"sha256": "638f08a42e9cf4996b5d07f69b4d6f517a518369f3604d25be1006e40e080765"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/language/semantic_source.rs",
|
||||
"sha256": "584b5bb59d87a7b8d44d5dd2d124c3a1323b21492b138a17bc9526c0087c9e29"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/lib.rs",
|
||||
"sha256": "52cb6ee7a4ffc00711ecf121c1a1b14ca154863f7ca27b0d2a8e239edce8aca7"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/migration_v2/adapter.rs",
|
||||
"sha256": "66741b4c8354a23b8a932ecc5bf1bc3bb6e74279558757b8c35bc2157a7e51b5"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/migration_v2/mod.rs",
|
||||
"sha256": "857d8bb13b6814daba6780ca770c16352943c01cb130245d055f11b3b957b3e9"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/migration_v2/model.rs",
|
||||
"sha256": "8ac63eb59b2e57bdecff2477ee0634096090924d3dd1851e1c454880a889dacb"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/migration_v2/references.rs",
|
||||
"sha256": "33a6819abc83139fdaebeebd1b553238a328637106cce7dd5a535f1791d43e20"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/migration_v2/source.rs",
|
||||
"sha256": "74fee9b24e7c912182c998ab9d9aeec125e2009c3214959229e844c67afe2bf6"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/migration_v2/state_validation.rs",
|
||||
"sha256": "e29ef071bab0a68460ddf55c46131c6f66c022d52c3c602c37c013c3649da1b0"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/migration_v2/transitions.rs",
|
||||
"sha256": "f74174c532ba56b57c80b719a11fb4adea0a78ccc2828fff72fe2b9eda5a10b1"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/migration_v2/validation.rs",
|
||||
"sha256": "6d5e4a4e2805ae5045bf789a0485e14a61c63346587bace2b4a91173a688f780"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/migration_v3/mod.rs",
|
||||
"sha256": "f4edb6a0b50ecfe8670973776d703047b064420cb1f05913129c00fc656b3722"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/migration_v4.rs",
|
||||
"sha256": "50c652964f0f309e7f3f54b6ef12c40d42fcecc0490f005995c713df1179af70"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/model.rs",
|
||||
"sha256": "4585c895c64c4a183941c92cf047a9e52ff0cbe41971e16e78d229ea9dc101fb"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/operations.rs",
|
||||
"sha256": "a5ffbbf54cd440316a106d1188bfb699b57dae59f466edcb33618c333cd19f41"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/persistence.rs",
|
||||
"sha256": "1c01474f906a63e552c0d590b272c95821c00a100b3e79e58e67dbbeac341a14"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/ports.rs",
|
||||
"sha256": "5cac12eea70a54ebebbd6be77b63ce92e3195325c4bc7d27705c518686cbac80"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/projection.rs",
|
||||
"sha256": "69ed12bace2cc5024e151752a174ab14034ac2d05eb741fcf13e9a27521c059b"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/projection/port_layout.rs",
|
||||
"sha256": "a08d17d10a58bb8b206dd001b19b91d5aa3a9f743b298a4da57d503223c20079"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/projection/state.rs",
|
||||
"sha256": "73a607bac713ec7ff49a509fefb1a53c7686dca93ee241ffc835564d743afb28"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/references.rs",
|
||||
"sha256": "93553c234f32da35f1f41a7b83ad81c471c9803efa78ca222cf7de3c2f86c0bb"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/routing.rs",
|
||||
"sha256": "c5ebb350ba76f731f5c48d968bfbba4b6ef68ec046eb5893d116c4637ef15080"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/semantic.rs",
|
||||
"sha256": "f9895dd5b8fb27d10d4cfbb41c3542ef2018365a589b8d5778e5674ae5a1f239"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/semantic/catalog.rs",
|
||||
"sha256": "1ff74fcca18084d178681d7ff38ee1ccac632c27c4de0595ba1ceab8067ab3a4"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/semantic/historical.rs",
|
||||
"sha256": "eaf3284bf234312698514cdb636bc8348e1b5d746cb16b18df777e77d312abfb"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/semantic/linked.rs",
|
||||
"sha256": "6f335b14e809aecfc35defcaf77cb821b080d1506dbc46297912b25af98c07a2"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/semantic/mutate.rs",
|
||||
"sha256": "dc2b4c4a6eb156fe468eaa8c85fd37a505c0cd8ea1f226cb69cd01689afd3d9d"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/semantic/review.rs",
|
||||
"sha256": "0c157d2c327e72e89cca3b423e71ee73798105c5bb7271439c1a101642c91e3e"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/semantic/types.rs",
|
||||
"sha256": "6922fe35089f12fad43ab0ce1c6886d01a3855f90b8e2543e99802ac5e6f1bbb"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/session.rs",
|
||||
"sha256": "1eb77b329b2986fc6f5dc0171e77fd860375cdfd8c769a1367b445d6c49ebe00"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/source_files.rs",
|
||||
"sha256": "669491dbddb8e8e6556161426dffa1a49ac1c826d31020bff0956d81a4d84683"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/source_project.rs",
|
||||
"sha256": "1394370bcc5908873b830c2c11312e6e1611af41f46c4f43ca0a6bcb713bb466"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/source_project/source_migration.rs",
|
||||
"sha256": "add463ae85c420f853d324b00a221f0f874164cc3f3ed6bbb07557a9ed7a8c95"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/source_project/source_structure.rs",
|
||||
"sha256": "9f1f83315856a7025fe662c0fbb13452f31613224a03663150d6225d1ddc162f"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/state_validation.rs",
|
||||
"sha256": "e28d7f74385e2a78763716b34491bd73a00bfeccc085970ceb130627e3faee18"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/topology_context.rs",
|
||||
"sha256": "16de69b297de95d93c73808621607833b713af811dd082f0169186ae1e5c3e2d"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/topology_context/analysis_tests.rs",
|
||||
"sha256": "17839263410815e96fe027d4eba3aae0a9ab3b82b16947e2b29ebe0d09782b56"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/topology_context/analysis.rs",
|
||||
"sha256": "dead6194f0aa60cc46045c0e5ee10dbdbb689158c38634dfbbbc45d2ba88ac65"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/transitions.rs",
|
||||
"sha256": "fadd1b07b89cae4117b91abb4f1ac9ea81ff818ef20f2175e76ee8cf21fb708d"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/transitions/communication.rs",
|
||||
"sha256": "2b476630a88edd9021b9156ca12a5402e01604095f0d4002afb452671903a028"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/validation.rs",
|
||||
"sha256": "7c1b4ae5e4ebba13e99af9158ccde138dfc5e9a7afb580e76fba72d0b64ba74e"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/validation/coverage.rs",
|
||||
"sha256": "b08e4b344ecad055714168c83beff7fe29ba4edc53edbffa2eae04e5580a4d5f"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/view_options_tests.rs",
|
||||
"sha256": "64aac44de2df42a1012239a3dd75c473daf0c8990b3170b7bf7e78a556080208"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/view_options.rs",
|
||||
"sha256": "421ede0a04c593ca1df0e6e0d06fabcfcc8113cdbcfb0adc7f5c8ef4d1724208"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/communication_boundary_scenarios.rs",
|
||||
"sha256": "e1100b8f363910a487d90c73acc7df543dc6d532158c3c63ad11cd3d40d8e12b"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/communication_means.rs",
|
||||
"sha256": "a25f2749180bc6f4beb1445bfad3351eedf0669131a7439b633ab804439e95a4"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/coverage_diagnostics.rs",
|
||||
"sha256": "e2e9c3996daab47f2bd23f5d8c48b4e372af33331152f5fc85b76f06d04fcdf4"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/language.rs",
|
||||
"sha256": "77f6cb2a69976bfb5f60b243b71c07fb0edc7505002c1e9e73fcd6ddf6d9496e"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/port_anchors.rs",
|
||||
"sha256": "efbc0fa18733b1aaac501f9785a4c392fc49c05c45a5778d26e9b5dd951ca6fa"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/port_migration.rs",
|
||||
"sha256": "1801c376a5435ac7efb2a8342348b2c7ffef930bba65deca4acb66b06e0483d7"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/port_source.rs",
|
||||
"sha256": "8d2ddaca4045d330f6eda2591561497b858aad53769a823249d0bea4f5916e12"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/prepared_commands.rs",
|
||||
"sha256": "52cab1c668064777412df0a6a3c20d74d579b33794b4b00525b9f151c06eac9b"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/process_diagnostics.rs",
|
||||
"sha256": "1dbbc590239ef7542be4a2bdc9543aaa64d108417b03dd312a0894e364745906"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/r3_constructor_ack.rs",
|
||||
"sha256": "e5c44676ea0aab97f2e091ddac2f4bc4a4a90f5877f7bbf08b61229600944dd0"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/r3_migration.rs",
|
||||
"sha256": "ebd04120f5215b3956fdc79ee78f6e38734c7d13392f1fc28aaba957912265bd"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/r3_semantic.rs",
|
||||
"sha256": "49c3763eaf2131070a7c46cc23a84f917d292d41610b6404510e5607f8890c32"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/r3_source.rs",
|
||||
"sha256": "f31f60659df3c7ef601650d1cff140589d8729f01a3f65a4a6a6c2272f15c408"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/r6_data.rs",
|
||||
"sha256": "5f7ffb3bc73c3c85423623fe56f8680646dc97040327bdbead9d47a37e80f155"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/r6_migration.rs",
|
||||
"sha256": "81519bc1512f5e069432e9a1cdad04c10216d492df01b4b75b02102480ebd6a1"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/r6_source.rs",
|
||||
"sha256": "88a0aa087a7678e2898ff6057538072ca92072190712f05fff08372cc56ebed6"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/release_capabilities.rs",
|
||||
"sha256": "b92cba2a562e78287ddd7dc3e94557848d17faa0c8a102601b50286b9af48a30"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/release_diagrams.rs",
|
||||
"sha256": "cefcc0582d91266d5de6ee5daefcfe41b606ab971ab235b59034a8e1dbc24b7d"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/release_language_limits.rs",
|
||||
"sha256": "8578da4f76bbd2d0e1bfe295ab1259e2162a3fe3d0d2ba8901915d7a40942806"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/release_ports.rs",
|
||||
"sha256": "506e62beab0c7ec0cece83e9e0c1a810fa1f99285c9e1cab0c77f06de55a40e1"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/release_routing.rs",
|
||||
"sha256": "ae168faaba23769b7ca1fd65f217b9db671726e67276eb5a04e5e72b1447a462"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/release_scenarios.rs",
|
||||
"sha256": "0f5d876badeec653fc7b12610a77d867e9a53582e605253deff8d379da21572f"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/release_states.rs",
|
||||
"sha256": "b1a26acee95eaba7f9920f9da738a1e33606552eb078e6d298895daff7e3d811"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/release_topology.rs",
|
||||
"sha256": "c51dbe5aa5a038f9ed830daa02aa67714472179eb7fd6da5ce36dc6931c8b3a9"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/release1_teaching.rs",
|
||||
"sha256": "be179290d871125dc97cfd2a3067c7ac89c8a538fdb0afafc5b825bcdfb2bef2"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/review_regressions.rs",
|
||||
"sha256": "f154e5c88d35e31512e2b07733a1327ea3e2fcfd1ef8eb23066a4e9573f387c1"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/route_source.rs",
|
||||
"sha256": "1a73fdbac0d0ae0e84d8d67b6ad398e11223958f5dca007f73757c91827decfb"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/routing_geometry.rs",
|
||||
"sha256": "2d8eea4a2bba535ed0ea6daabccdaf87b375828c404baa10518c7bd7fd6253d3"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/routing_labels.rs",
|
||||
"sha256": "dff884d799c3c5c9257210248c55767e294e3cb317c605cbd0ddc9600c216eb6"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/semantic_artifact_absence_oracle.rs",
|
||||
"sha256": "d93eaf9667f4221232168bef0fe9f069e5be1b1f23410bda5dd67010b96e5c8a"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/semantic_attachments_gui_oracle.rs",
|
||||
"sha256": "7f526d363ba05e7ac89faf0fead0796e419c34ee204edf44a1b46c78308b0970"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/source_files.rs",
|
||||
"sha256": "f0429a0b89a443458ea9af7083c1e4e7411417a7e6b47e618bfbb9266dc164f9"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/source_identity_updates.rs",
|
||||
"sha256": "f52aa38b4d317e532c641ad7463472e4b40e802e60052cf0017556ffc63da06c"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/source_nested_updates.rs",
|
||||
"sha256": "9ac3390936c0d250651ea94a1b8faf0adfb680f061ececc56f2a52909b5ba855"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/source_project.rs",
|
||||
"sha256": "cef58adb16aa4d2527ddc0628aec3e26f0b42b4be70014a7e360fafdac13a942"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/source6_view_contract.rs",
|
||||
"sha256": "fa566de53946d40bbbd9033edfeed177180c4dc44afa2f2f4667ffdc622eb3bb"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/source7_contract.rs",
|
||||
"sha256": "cad4f1a135ddbd947bdf3c71d1b44f1e438725095ea8c4d893464bc721c2363f"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/state_diagrams.rs",
|
||||
"sha256": "6b02b387a3ae559cb3ad72de24292a5abcb76aaac0adbab5d2aa5211b22cd2e5"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/workflows.rs",
|
||||
"sha256": "bd843dfddbd5724ec08f657571f887496203056fe2fa5864ef50a168a613b89b"
|
||||
}
|
||||
],
|
||||
"binary_sha256": {
|
||||
"arcadia.exe": "bad169f8be78dcfa74b32714569c886dd2132727080684fc2ba618d5822e3559",
|
||||
"libunwind.dll": "f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4"
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"source_identity": "CLI semantic/build-input basis 01b76a1e89e93b7992f39e63560aa8b2ca2f69e0. Six original CLI manifest/workspace/lock/source/test hashes and 95 unchanged core/shared build inputs independently matched. Packaging assembly basis a0a48e82bd43be181caca1fe2c1514c21f2bc405 changes packaging only and is not claimed as the original compiler input commit.",
|
||||
"profile": "Preserved Windows debug/development artifact from cargo build --locked --offline -p oa-cli --bin arcadia, successful September11 2026 11:45:43.6035381Z,9.7336637seconds. No release-profile or reproducible-build claim.",
|
||||
"compiler": "rustc1.98.1 commit48a229ceaefd4985c50990b14116b6d856af0985,x86_64-pc-windows-gnullvm,LLVM22.1.8. Frozen executable embeds that compiler path; current toolchain target-library hashes match preserved inventory. Original compile/test/lint and independent frozen-binary review records retained separately.",
|
||||
"target": "x86_64-pc-windows-gnullvm",
|
||||
"binary_sha256": {
|
||||
"arcadia.exe": "bad169f8be78dcfa74b32714569c886dd2132727080684fc2ba618d5822e3559"
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
[
|
||||
{
|
||||
"path": "assembly.json",
|
||||
"original": ".local/cli-packaging-20260911/assembly.json",
|
||||
"bytes": 17452,
|
||||
"sha256": "284ecc5e3caaf9e2201785619f81ad200166e7d489c2a65276857f71df7f0dc0"
|
||||
},
|
||||
{
|
||||
"path": "preparation.json",
|
||||
"original": ".local/cli-packaging-20260911/preparation.json",
|
||||
"bytes": 17497,
|
||||
"sha256": "0ec70eef7f487693c4e698cbe54d2b99e6f4d3fc10bb1e7edabc3d236c2a7aff"
|
||||
},
|
||||
{
|
||||
"path": "build-evidence.json",
|
||||
"original": ".local/cli-packaging-20260911/build-evidence.json",
|
||||
"bytes": 1086,
|
||||
"sha256": "b75c6f0b78caf508c046de1dd7e67a1aade15924d50452635da8b6b1eba4a7d5"
|
||||
},
|
||||
{
|
||||
"path": "package-initial-result.json",
|
||||
"original": ".local/cli-packaging-20260911/package-initial-result.json",
|
||||
"bytes": 216,
|
||||
"sha256": "8c474fc7c7ce254fc17c0df9c0e46bb23403bc11be052fda121e49184157dc15"
|
||||
},
|
||||
{
|
||||
"path": "windows-initial.log",
|
||||
"original": ".local/cli-packaging-20260911/windows-initial.log",
|
||||
"bytes": 131,
|
||||
"sha256": "c74a669d68754f45a1649636d70cab82132569ec53810a63c490bfd430e451cf"
|
||||
},
|
||||
{
|
||||
"path": "notices-initial.log",
|
||||
"original": ".local/cli-packaging-20260911/notices-initial.log",
|
||||
"bytes": 430,
|
||||
"sha256": "f37409b4d36959bc03dcb35ec053301da9cf4c6effbb6e9a68fca534133c36b0"
|
||||
},
|
||||
{
|
||||
"path": "artifacts-final.log",
|
||||
"original": ".local/cli-packaging-20260911/artifacts-final.log",
|
||||
"bytes": 117,
|
||||
"sha256": "729fe10826e06b3b0997d6c4879349f388978a5fcb5bea62d55cc4ee6603cc16"
|
||||
},
|
||||
{
|
||||
"path": "reviewer.py",
|
||||
"original": ".local/cli-packaging-review-20260911/review.py",
|
||||
"bytes": 18543,
|
||||
"sha256": "f85d1f34ab75b4067efe09886e447bf9fd54dc232020bcb1a46404ec57b2351e"
|
||||
},
|
||||
{
|
||||
"path": "reviewer-result.json",
|
||||
"original": ".local/cli-packaging-review-20260911/result.json",
|
||||
"bytes": 33967,
|
||||
"sha256": "3b92a816a62de386b5927b208c79c2a7c541e6e2e2e30bde31b4c11bbbc4509e"
|
||||
}
|
||||
]
|
||||
@@ -1,2 +0,0 @@
|
||||
PASS: normal/build and server-only Linux selection, cross-target runtime refusal, Rust reports, actual source hashes, stale archive refusal, doc-comment font exclusion, production font path/mapping refusals, exact font mapping and changed-byte refusal, unsupported source refusal, traversal refusal, altered supplement refusal.
|
||||
Synthetic fixture retained for inspection: C:\Temp\arcadia-notices-c72f98e0978c45618416fe3d2cfdd38e
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"started": "2026-09-11T12:36:52.6900250Z",
|
||||
"assembly_commit": "a0a48e82bd43be181caca1fe2c1514c21f2bc405",
|
||||
"success": true,
|
||||
"finished": "2026-09-11T12:37:33.7683084Z",
|
||||
"elapsed_seconds": 41.084165
|
||||
}
|
||||
@@ -1,454 +0,0 @@
|
||||
{
|
||||
"semantic_source_commit": "01b76a1e89e93b7992f39e63560aa8b2ca2f69e0",
|
||||
"original_cli_manifest_sha256": "dd73fc2bb2190b23fffe16d6c63d4acddf52338062fac298379ddfd241d82eb8",
|
||||
"exact_cli_inputs": [
|
||||
{
|
||||
"path": "Cargo.toml",
|
||||
"sha256": "369f5b4b2b60705a94623c8c0b68f8e0b048f263280ce07ae0629ad353797d62"
|
||||
},
|
||||
{
|
||||
"path": "Cargo.lock",
|
||||
"sha256": "2b960242c33eb839ff008d01731f6d5cdb4e047ca8cd6e13151cbf9c09913510"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-cli/Cargo.toml",
|
||||
"sha256": "49c567f1af74a826683b4d96845feb3806fd9bcb7dc2767e16b5509d4e5e1cde"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-cli/src/lib.rs",
|
||||
"sha256": "97bebab63b4a611a1d36ad0c20fe452cbe1985fe3a6fbd39c954603da11410fc"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-cli/src/main.rs",
|
||||
"sha256": "de964f9b6c59c0f31c080afbd3b31d442b07b1b44f6a187a248d3409c45d35fb"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-cli/tests/source_cli.rs",
|
||||
"sha256": "a3a1b856c7a9fd3d80bc8074150a24fbb0ef0e9ddf736c7cea036725fc3dc2b8"
|
||||
}
|
||||
],
|
||||
"unchanged_core_shared_build_inputs": [
|
||||
{
|
||||
"path": "build/windows_manifest.rs",
|
||||
"sha256": "168da2c42ecccfebc11ee3e1b455e653a68fe9c6d30a9842a8b9e7441d086c80"
|
||||
},
|
||||
{
|
||||
"path": "build/windows.manifest",
|
||||
"sha256": "9c19c5a02f858ece84c44839ad2b145d9c1d90442da0b15c87fd045f975974eb"
|
||||
},
|
||||
{
|
||||
"path": "build/windows.rc",
|
||||
"sha256": "a27385edcae0bcf96f55361056cb7be5f36d76fcddcaefd20647c91959563e1e"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/build.rs",
|
||||
"sha256": "ab1be6ae1fd8bf3ebaeef6348347a9d5e666bde693126f199e041659e0b094a9"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/Cargo.toml",
|
||||
"sha256": "64824ec447be777841a972bf06c894ef2624a37e4725583bcfe0389deb9f00a3"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/examples/generate_demo.rs",
|
||||
"sha256": "e26f432c95331aeca50985dbb3ad2c558affd43023b585811f0f7bfaa59f6661"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/examples/generate_release1.rs",
|
||||
"sha256": "1d5f11165d74893e4bc18dbf3a2f2111f4026bd89fd06ee7f0bae69e45c4d4b5"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/examples/release_dense_views.rs",
|
||||
"sha256": "a83158488ef9b7394f136c52b1496719be935c4e5bdfe457cab5c9894f5f9412"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/examples/routing_workshop.rs",
|
||||
"sha256": "571307b18b6d4fa04700c5a4d4b0c2154c419e93a21292b2028e7b6f46a2faa7"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/data.rs",
|
||||
"sha256": "7f9c7c7dd8353cb5a38597b92cde6d3e890203d392473187eb387d41ff37414a"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/demo.rs",
|
||||
"sha256": "ad62123f96c20ff023cafc1a9849b0fa06bc62edd1fb9701799e4599b458600c"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/language_nesting_tests.rs",
|
||||
"sha256": "bd061611e8cb6f28ab203c73e544ac98bf275eb464cd314ad5fbd25f773df33b"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/language.rs",
|
||||
"sha256": "638f08a42e9cf4996b5d07f69b4d6f517a518369f3604d25be1006e40e080765"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/language/semantic_source.rs",
|
||||
"sha256": "584b5bb59d87a7b8d44d5dd2d124c3a1323b21492b138a17bc9526c0087c9e29"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/lib.rs",
|
||||
"sha256": "52cb6ee7a4ffc00711ecf121c1a1b14ca154863f7ca27b0d2a8e239edce8aca7"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/migration_v2/adapter.rs",
|
||||
"sha256": "66741b4c8354a23b8a932ecc5bf1bc3bb6e74279558757b8c35bc2157a7e51b5"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/migration_v2/mod.rs",
|
||||
"sha256": "857d8bb13b6814daba6780ca770c16352943c01cb130245d055f11b3b957b3e9"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/migration_v2/model.rs",
|
||||
"sha256": "8ac63eb59b2e57bdecff2477ee0634096090924d3dd1851e1c454880a889dacb"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/migration_v2/references.rs",
|
||||
"sha256": "33a6819abc83139fdaebeebd1b553238a328637106cce7dd5a535f1791d43e20"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/migration_v2/source.rs",
|
||||
"sha256": "74fee9b24e7c912182c998ab9d9aeec125e2009c3214959229e844c67afe2bf6"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/migration_v2/state_validation.rs",
|
||||
"sha256": "e29ef071bab0a68460ddf55c46131c6f66c022d52c3c602c37c013c3649da1b0"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/migration_v2/transitions.rs",
|
||||
"sha256": "f74174c532ba56b57c80b719a11fb4adea0a78ccc2828fff72fe2b9eda5a10b1"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/migration_v2/validation.rs",
|
||||
"sha256": "6d5e4a4e2805ae5045bf789a0485e14a61c63346587bace2b4a91173a688f780"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/migration_v3/mod.rs",
|
||||
"sha256": "f4edb6a0b50ecfe8670973776d703047b064420cb1f05913129c00fc656b3722"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/migration_v4.rs",
|
||||
"sha256": "50c652964f0f309e7f3f54b6ef12c40d42fcecc0490f005995c713df1179af70"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/model.rs",
|
||||
"sha256": "4585c895c64c4a183941c92cf047a9e52ff0cbe41971e16e78d229ea9dc101fb"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/operations.rs",
|
||||
"sha256": "a5ffbbf54cd440316a106d1188bfb699b57dae59f466edcb33618c333cd19f41"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/persistence.rs",
|
||||
"sha256": "1c01474f906a63e552c0d590b272c95821c00a100b3e79e58e67dbbeac341a14"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/ports.rs",
|
||||
"sha256": "5cac12eea70a54ebebbd6be77b63ce92e3195325c4bc7d27705c518686cbac80"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/projection.rs",
|
||||
"sha256": "69ed12bace2cc5024e151752a174ab14034ac2d05eb741fcf13e9a27521c059b"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/projection/port_layout.rs",
|
||||
"sha256": "a08d17d10a58bb8b206dd001b19b91d5aa3a9f743b298a4da57d503223c20079"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/projection/state.rs",
|
||||
"sha256": "73a607bac713ec7ff49a509fefb1a53c7686dca93ee241ffc835564d743afb28"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/references.rs",
|
||||
"sha256": "93553c234f32da35f1f41a7b83ad81c471c9803efa78ca222cf7de3c2f86c0bb"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/routing.rs",
|
||||
"sha256": "c5ebb350ba76f731f5c48d968bfbba4b6ef68ec046eb5893d116c4637ef15080"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/semantic.rs",
|
||||
"sha256": "f9895dd5b8fb27d10d4cfbb41c3542ef2018365a589b8d5778e5674ae5a1f239"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/semantic/catalog.rs",
|
||||
"sha256": "1ff74fcca18084d178681d7ff38ee1ccac632c27c4de0595ba1ceab8067ab3a4"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/semantic/historical.rs",
|
||||
"sha256": "eaf3284bf234312698514cdb636bc8348e1b5d746cb16b18df777e77d312abfb"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/semantic/linked.rs",
|
||||
"sha256": "6f335b14e809aecfc35defcaf77cb821b080d1506dbc46297912b25af98c07a2"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/semantic/mutate.rs",
|
||||
"sha256": "dc2b4c4a6eb156fe468eaa8c85fd37a505c0cd8ea1f226cb69cd01689afd3d9d"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/semantic/review.rs",
|
||||
"sha256": "0c157d2c327e72e89cca3b423e71ee73798105c5bb7271439c1a101642c91e3e"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/semantic/types.rs",
|
||||
"sha256": "6922fe35089f12fad43ab0ce1c6886d01a3855f90b8e2543e99802ac5e6f1bbb"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/session.rs",
|
||||
"sha256": "1eb77b329b2986fc6f5dc0171e77fd860375cdfd8c769a1367b445d6c49ebe00"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/source_files.rs",
|
||||
"sha256": "669491dbddb8e8e6556161426dffa1a49ac1c826d31020bff0956d81a4d84683"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/source_project.rs",
|
||||
"sha256": "1394370bcc5908873b830c2c11312e6e1611af41f46c4f43ca0a6bcb713bb466"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/source_project/source_migration.rs",
|
||||
"sha256": "add463ae85c420f853d324b00a221f0f874164cc3f3ed6bbb07557a9ed7a8c95"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/source_project/source_structure.rs",
|
||||
"sha256": "9f1f83315856a7025fe662c0fbb13452f31613224a03663150d6225d1ddc162f"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/state_validation.rs",
|
||||
"sha256": "e28d7f74385e2a78763716b34491bd73a00bfeccc085970ceb130627e3faee18"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/topology_context.rs",
|
||||
"sha256": "16de69b297de95d93c73808621607833b713af811dd082f0169186ae1e5c3e2d"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/topology_context/analysis_tests.rs",
|
||||
"sha256": "17839263410815e96fe027d4eba3aae0a9ab3b82b16947e2b29ebe0d09782b56"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/topology_context/analysis.rs",
|
||||
"sha256": "dead6194f0aa60cc46045c0e5ee10dbdbb689158c38634dfbbbc45d2ba88ac65"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/transitions.rs",
|
||||
"sha256": "fadd1b07b89cae4117b91abb4f1ac9ea81ff818ef20f2175e76ee8cf21fb708d"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/transitions/communication.rs",
|
||||
"sha256": "2b476630a88edd9021b9156ca12a5402e01604095f0d4002afb452671903a028"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/validation.rs",
|
||||
"sha256": "7c1b4ae5e4ebba13e99af9158ccde138dfc5e9a7afb580e76fba72d0b64ba74e"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/validation/coverage.rs",
|
||||
"sha256": "b08e4b344ecad055714168c83beff7fe29ba4edc53edbffa2eae04e5580a4d5f"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/view_options_tests.rs",
|
||||
"sha256": "64aac44de2df42a1012239a3dd75c473daf0c8990b3170b7bf7e78a556080208"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/src/view_options.rs",
|
||||
"sha256": "421ede0a04c593ca1df0e6e0d06fabcfcc8113cdbcfb0adc7f5c8ef4d1724208"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/communication_boundary_scenarios.rs",
|
||||
"sha256": "e1100b8f363910a487d90c73acc7df543dc6d532158c3c63ad11cd3d40d8e12b"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/communication_means.rs",
|
||||
"sha256": "a25f2749180bc6f4beb1445bfad3351eedf0669131a7439b633ab804439e95a4"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/coverage_diagnostics.rs",
|
||||
"sha256": "e2e9c3996daab47f2bd23f5d8c48b4e372af33331152f5fc85b76f06d04fcdf4"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/language.rs",
|
||||
"sha256": "77f6cb2a69976bfb5f60b243b71c07fb0edc7505002c1e9e73fcd6ddf6d9496e"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/port_anchors.rs",
|
||||
"sha256": "efbc0fa18733b1aaac501f9785a4c392fc49c05c45a5778d26e9b5dd951ca6fa"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/port_migration.rs",
|
||||
"sha256": "1801c376a5435ac7efb2a8342348b2c7ffef930bba65deca4acb66b06e0483d7"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/port_source.rs",
|
||||
"sha256": "8d2ddaca4045d330f6eda2591561497b858aad53769a823249d0bea4f5916e12"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/prepared_commands.rs",
|
||||
"sha256": "52cab1c668064777412df0a6a3c20d74d579b33794b4b00525b9f151c06eac9b"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/process_diagnostics.rs",
|
||||
"sha256": "1dbbc590239ef7542be4a2bdc9543aaa64d108417b03dd312a0894e364745906"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/r3_constructor_ack.rs",
|
||||
"sha256": "e5c44676ea0aab97f2e091ddac2f4bc4a4a90f5877f7bbf08b61229600944dd0"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/r3_migration.rs",
|
||||
"sha256": "ebd04120f5215b3956fdc79ee78f6e38734c7d13392f1fc28aaba957912265bd"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/r3_semantic.rs",
|
||||
"sha256": "49c3763eaf2131070a7c46cc23a84f917d292d41610b6404510e5607f8890c32"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/r3_source.rs",
|
||||
"sha256": "f31f60659df3c7ef601650d1cff140589d8729f01a3f65a4a6a6c2272f15c408"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/r6_data.rs",
|
||||
"sha256": "5f7ffb3bc73c3c85423623fe56f8680646dc97040327bdbead9d47a37e80f155"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/r6_migration.rs",
|
||||
"sha256": "81519bc1512f5e069432e9a1cdad04c10216d492df01b4b75b02102480ebd6a1"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/r6_source.rs",
|
||||
"sha256": "88a0aa087a7678e2898ff6057538072ca92072190712f05fff08372cc56ebed6"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/release_capabilities.rs",
|
||||
"sha256": "b92cba2a562e78287ddd7dc3e94557848d17faa0c8a102601b50286b9af48a30"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/release_diagrams.rs",
|
||||
"sha256": "cefcc0582d91266d5de6ee5daefcfe41b606ab971ab235b59034a8e1dbc24b7d"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/release_language_limits.rs",
|
||||
"sha256": "8578da4f76bbd2d0e1bfe295ab1259e2162a3fe3d0d2ba8901915d7a40942806"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/release_ports.rs",
|
||||
"sha256": "506e62beab0c7ec0cece83e9e0c1a810fa1f99285c9e1cab0c77f06de55a40e1"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/release_routing.rs",
|
||||
"sha256": "ae168faaba23769b7ca1fd65f217b9db671726e67276eb5a04e5e72b1447a462"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/release_scenarios.rs",
|
||||
"sha256": "0f5d876badeec653fc7b12610a77d867e9a53582e605253deff8d379da21572f"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/release_states.rs",
|
||||
"sha256": "b1a26acee95eaba7f9920f9da738a1e33606552eb078e6d298895daff7e3d811"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/release_topology.rs",
|
||||
"sha256": "c51dbe5aa5a038f9ed830daa02aa67714472179eb7fd6da5ce36dc6931c8b3a9"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/release1_teaching.rs",
|
||||
"sha256": "be179290d871125dc97cfd2a3067c7ac89c8a538fdb0afafc5b825bcdfb2bef2"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/review_regressions.rs",
|
||||
"sha256": "f154e5c88d35e31512e2b07733a1327ea3e2fcfd1ef8eb23066a4e9573f387c1"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/route_source.rs",
|
||||
"sha256": "1a73fdbac0d0ae0e84d8d67b6ad398e11223958f5dca007f73757c91827decfb"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/routing_geometry.rs",
|
||||
"sha256": "2d8eea4a2bba535ed0ea6daabccdaf87b375828c404baa10518c7bd7fd6253d3"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/routing_labels.rs",
|
||||
"sha256": "dff884d799c3c5c9257210248c55767e294e3cb317c605cbd0ddc9600c216eb6"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/semantic_artifact_absence_oracle.rs",
|
||||
"sha256": "d93eaf9667f4221232168bef0fe9f069e5be1b1f23410bda5dd67010b96e5c8a"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/semantic_attachments_gui_oracle.rs",
|
||||
"sha256": "7f526d363ba05e7ac89faf0fead0796e419c34ee204edf44a1b46c78308b0970"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/source_files.rs",
|
||||
"sha256": "f0429a0b89a443458ea9af7083c1e4e7411417a7e6b47e618bfbb9266dc164f9"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/source_identity_updates.rs",
|
||||
"sha256": "f52aa38b4d317e532c641ad7463472e4b40e802e60052cf0017556ffc63da06c"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/source_nested_updates.rs",
|
||||
"sha256": "9ac3390936c0d250651ea94a1b8faf0adfb680f061ececc56f2a52909b5ba855"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/source_project.rs",
|
||||
"sha256": "cef58adb16aa4d2527ddc0628aec3e26f0b42b4be70014a7e360fafdac13a942"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/source6_view_contract.rs",
|
||||
"sha256": "fa566de53946d40bbbd9033edfeed177180c4dc44afa2f2f4667ffdc622eb3bb"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/source7_contract.rs",
|
||||
"sha256": "cad4f1a135ddbd947bdf3c71d1b44f1e438725095ea8c4d893464bc721c2363f"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/state_diagrams.rs",
|
||||
"sha256": "6b02b387a3ae559cb3ad72de24292a5abcb76aaac0adbab5d2aa5211b22cd2e5"
|
||||
},
|
||||
{
|
||||
"path": "crates/oa-core/tests/workflows.rs",
|
||||
"sha256": "bd843dfddbd5724ec08f657571f887496203056fe2fa5864ef50a168a613b89b"
|
||||
}
|
||||
],
|
||||
"packaging_files": [
|
||||
{
|
||||
"path": "scripts/package-cli-windows.ps1",
|
||||
"sha256": "b6ec59bb55ff1e5624c782f492ce1d169131e708f257dcca195957edf7341c1a"
|
||||
},
|
||||
{
|
||||
"path": "scripts/package-common.ps1",
|
||||
"sha256": "d859453a8230b4f305d822c8742b371a71e35df698109a5d55c948f6dfaf0640"
|
||||
},
|
||||
{
|
||||
"path": "scripts/package-windows.ps1",
|
||||
"sha256": "28997ff78565619ed9c9b185f66dc922808f1f0be17f522b430c6c415448663d"
|
||||
},
|
||||
{
|
||||
"path": "scripts/windows-notices.ps1",
|
||||
"sha256": "a5328c05579e1b9ca09142ee99e87c12994d12764f7c69448e392883d718ef01"
|
||||
},
|
||||
{
|
||||
"path": "scripts/package-artifacts.py",
|
||||
"sha256": "24515fdbf4eebfb47b82383c6b3ccf290261cbd919f7f46a586b6488af769c8d"
|
||||
},
|
||||
{
|
||||
"path": "scripts/package-cli-readme.md",
|
||||
"sha256": "39171afa7dab4be10a8566153d7384aa439f1494851ce2ca1da989c6f370ef2b"
|
||||
},
|
||||
{
|
||||
"path": "scripts/test-package-cli-windows.ps1",
|
||||
"sha256": "2c18f91149ede5c7b677a165bd0d8b31f3c4691642cc10dd1007a7a77bde912a"
|
||||
},
|
||||
{
|
||||
"path": "scripts/test-package-artifacts.py",
|
||||
"sha256": "881718181a36fa5f7abfcd2e746bc1339446f9aeed6b2c039b8d2ce230ec202d"
|
||||
},
|
||||
{
|
||||
"path": "scripts/test-windows-notices.ps1",
|
||||
"sha256": "00a6ad1ed28fdb9044ceb7cc0714ceb7af095db2fb18f4828a053aa872a3147d"
|
||||
}
|
||||
],
|
||||
"binary_sha256": {
|
||||
"arcadia.exe": "bad169f8be78dcfa74b32714569c886dd2132727080684fc2ba618d5822e3559",
|
||||
"libunwind.dll": "f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4"
|
||||
}
|
||||
}
|
||||
@@ -1,458 +0,0 @@
|
||||
{
|
||||
"started_utc": "2026-09-11T12:42:56Z",
|
||||
"checks": [
|
||||
"preserve earlier evidence; no overwrite",
|
||||
"all original ELF/tar/document/notice helpers unchanged outside product selection",
|
||||
"windows actual full Studio document closure unchanged",
|
||||
"linux actual full Studio document closure unchanged",
|
||||
"only notice source delta is oa-cli allowed-root check; default/body unchanged",
|
||||
"Linux entry/deployment unchanged: scripts/package-linux.ps1",
|
||||
"Linux entry/deployment unchanged: deployment/Dockerfile",
|
||||
"Linux entry/deployment unchanged: deployment/Dockerfile.dockerignore",
|
||||
"PE reader extraction is byte-equivalent after function rename/EOL normalization",
|
||||
"Windows system DLL allowlist unchanged",
|
||||
"Studio explicitly retains both original binary inputs",
|
||||
"Studio post-inspection packaging pipeline unchanged",
|
||||
"assembly delta exactly nine packaging/script paths",
|
||||
"reviewed main packaging sources equal frozen assembly bytes",
|
||||
"fresh extraction directory",
|
||||
"frozen ZIP size and hash",
|
||||
"114 unique ZIP files",
|
||||
"single safe normalized Windows ZIP root",
|
||||
"ordinary fixed-time ZIP members and CRCs",
|
||||
"complete clean extraction equals ZIP bytes",
|
||||
"complete checksum inventory equals every other extracted file",
|
||||
"all package inputs plus exactly six generated records",
|
||||
"frozen CLI and runtime hashes",
|
||||
"no unrelated executable payload",
|
||||
"exact public document/example: LICENSE",
|
||||
"exact public document/example: docs/api.rst",
|
||||
"exact public document/example: docs/language.html",
|
||||
"exact public document/example: docs/language.rst",
|
||||
"exact public document/example: docs/source-cli.md",
|
||||
"exact public document/example: examples/release1/expected-diagnostics.json",
|
||||
"exact public document/example: examples/release1/model.json",
|
||||
"exact public document/example: examples/release1/model.rs",
|
||||
"exact public document/example: examples/release1/project.arcadia",
|
||||
"exact public document/example: examples/release1/README.md",
|
||||
"exact public document/example: examples/release1/traceability.arcadia",
|
||||
"exact public document/example: examples/release1/transition-before.json",
|
||||
"exact public document/example: examples/release1/transition-request.json",
|
||||
"exact public document/example: examples/text-first/project.arcadia",
|
||||
"exact public document/example: examples/text-first/README.md",
|
||||
"exact public document/example: examples/release1/evidence/repair-check.md",
|
||||
"exact public document/example: examples/release1/oa/capabilities.arcadia",
|
||||
"exact public document/example: examples/release1/oa/model.arcadia",
|
||||
"exact public document/example: examples/release1/oa/participants.arcadia",
|
||||
"exact public document/example: examples/release1/oa/types.arcadia",
|
||||
"exact public document/example: examples/release1/sa/capabilities.arcadia",
|
||||
"exact public document/example: examples/release1/sa/model.arcadia",
|
||||
"exact public document/example: examples/release1/sa/participants.arcadia",
|
||||
"exact public document/example: examples/release1/sa/types.arcadia",
|
||||
"exact public document/example: examples/release1/views/diagram_oa_allocation_view.arcadia",
|
||||
"exact public document/example: examples/release1/views/diagram_sa_allocation_view.arcadia",
|
||||
"exact public document/example: examples/release1/views/diagram_sa_capability_view.arcadia",
|
||||
"exact public document/example: examples/release1/views/diagram_sa_service_view.arcadia",
|
||||
"exact public document/example: examples/release1/views/diagram_sa_state_view.arcadia",
|
||||
"exact public document/example: examples/release1/oa/activities/activity_oa_restore_work.arcadia",
|
||||
"exact public document/example: examples/release1/oa/behavior/process_oa_assessment_procedure.arcadia",
|
||||
"exact public document/example: examples/release1/oa/behavior/process_oa_field_procedure.arcadia",
|
||||
"exact public document/example: examples/release1/oa/behavior/process_oa_response.arcadia",
|
||||
"exact public document/example: examples/release1/oa/behavior/scenario_oa_conversation.arcadia",
|
||||
"exact public document/example: examples/release1/oa/behavior/scenario_oa_full_scenario.arcadia",
|
||||
"exact public document/example: examples/release1/oa/behavior/state_machine_oa_monitor.arcadia",
|
||||
"exact public document/example: examples/release1/sa/behavior/process_sa_assessment_procedure.arcadia",
|
||||
"exact public document/example: examples/release1/sa/behavior/process_sa_field_procedure.arcadia",
|
||||
"exact public document/example: examples/release1/sa/behavior/process_sa_response.arcadia",
|
||||
"exact public document/example: examples/release1/sa/behavior/scenario_sa_conversation.arcadia",
|
||||
"exact public document/example: examples/release1/sa/behavior/scenario_sa_full_scenario.arcadia",
|
||||
"exact public document/example: examples/release1/sa/behavior/state_machine_sa_monitor.arcadia",
|
||||
"exact public document/example: examples/release1/sa/functions/function_sa_audit_thresholds.arcadia",
|
||||
"exact public document/example: examples/release1/sa/functions/function_sa_restore_work.arcadia",
|
||||
"CLI-specific README",
|
||||
"27 dependency packages; only CLI root",
|
||||
"original CLI Cargo lock identity",
|
||||
"locked dependency: oa-cli",
|
||||
"complete local crate inputs: oa-cli",
|
||||
"locked dependency: oa-core",
|
||||
"complete local crate inputs: oa-core",
|
||||
"locked dependency: cfg-if",
|
||||
"locked dependency: fastrand",
|
||||
"locked dependency: getrandom",
|
||||
"locked dependency: itoa",
|
||||
"locked dependency: memchr",
|
||||
"locked dependency: once_cell",
|
||||
"locked dependency: proc-macro2",
|
||||
"locked dependency: quote",
|
||||
"locked dependency: rand_core",
|
||||
"locked dependency: same-file",
|
||||
"locked dependency: serde_core",
|
||||
"locked dependency: serde_derive",
|
||||
"locked dependency: serde_json",
|
||||
"locked dependency: serde",
|
||||
"locked dependency: sha1_smol",
|
||||
"locked dependency: syn",
|
||||
"locked dependency: tempfile",
|
||||
"locked dependency: thiserror-impl",
|
||||
"locked dependency: thiserror",
|
||||
"locked dependency: unicode-ident",
|
||||
"locked dependency: uuid",
|
||||
"locked dependency: winapi-util",
|
||||
"locked dependency: windows-link",
|
||||
"locked dependency: windows-sys",
|
||||
"locked dependency: zmij",
|
||||
"all package/compiler/runtime notice references and bytes",
|
||||
"runtime notice binds packaged DLL",
|
||||
"all shared build input hashes",
|
||||
"six original CLI manifest inputs",
|
||||
"95 original core/shared build inputs",
|
||||
"CLI expected exit: ['--version']",
|
||||
"underlying CLI version",
|
||||
"CLI expected exit: ['--help']",
|
||||
"packaged help has command/preview contract",
|
||||
"CLI expected exit: ['check', WindowsPath('C:/Users/User/Documents/Git/ARCADIA-Studio/.local/cli-packaging-review-20260911/runtime/examples/text-first/project.arcadia'), '--json']",
|
||||
"JSON invocation keeps diagnostic envelope on stdout",
|
||||
"versioned JSON report agrees with process exit",
|
||||
"packaged example check: text-first/project.arcadia",
|
||||
"CLI expected exit: ['check', WindowsPath('C:/Users/User/Documents/Git/ARCADIA-Studio/.local/cli-packaging-review-20260911/runtime/examples/release1/project.arcadia'), '--json']",
|
||||
"JSON invocation keeps diagnostic envelope on stdout",
|
||||
"versioned JSON report agrees with process exit",
|
||||
"packaged example check: release1/project.arcadia",
|
||||
"checks preserve complete copied examples byte-for-byte",
|
||||
"CLI expected exit: ['stabilize', WindowsPath('C:/Users/User/Documents/Git/ARCADIA-Studio/.local/cli-packaging-review-20260911/runtime/exact-source/project.arcadia'), '--json']",
|
||||
"JSON invocation keeps diagnostic envelope on stdout",
|
||||
"versioned JSON report agrees with process exit",
|
||||
"stabilization is preview by default",
|
||||
"only missing-ID source changes; exact captured raw bytes",
|
||||
"fresh ID is attached to literal intended activity",
|
||||
"allocated identity differs from every preexisting identity",
|
||||
"stabilization preserves every other character, CRLF, comment, name and reference",
|
||||
"preview preserves all physical source bytes including detached file",
|
||||
"CLI expected exit: ['stabilize', WindowsPath('C:/Users/User/Documents/Git/ARCADIA-Studio/.local/cli-packaging-review-20260911/runtime/exact-source/project.arcadia'), '--write', '--json']",
|
||||
"JSON invocation keeps diagnostic envelope on stdout",
|
||||
"versioned JSON report agrees with process exit",
|
||||
"explicit stabilization writes",
|
||||
"only missing-ID source changes; exact captured raw bytes",
|
||||
"fresh ID is attached to literal intended activity",
|
||||
"allocated identity differs from every preexisting identity",
|
||||
"stabilization preserves every other character, CRLF, comment, name and reference",
|
||||
"stabilized complete physical file inventory and bytes",
|
||||
"CLI expected exit: ['rename-alias', WindowsPath('C:/Users/User/Documents/Git/ARCADIA-Studio/.local/cli-packaging-review-20260911/runtime/exact-source/project.arcadia'), 'crew', 'rescue_team', '--json']",
|
||||
"JSON invocation keeps diagnostic envelope on stdout",
|
||||
"versioned JSON report agrees with process exit",
|
||||
"alias preview changes exact declaration and typed reference only",
|
||||
"rename preview retains exact source bytes and identities",
|
||||
"CLI expected exit: ['rename-alias', WindowsPath('C:/Users/User/Documents/Git/ARCADIA-Studio/.local/cli-packaging-review-20260911/runtime/exact-source/project.arcadia'), 'crew', 'rescue_team', '--write', '--json']",
|
||||
"JSON invocation keeps diagnostic envelope on stdout",
|
||||
"versioned JSON report agrees with process exit",
|
||||
"explicit alias write matches full independent raw expected files",
|
||||
"all four identities remain ordered and exact through rename",
|
||||
"CLI expected exit: ['stabilize', WindowsPath('C:/Users/User/Documents/Git/ARCADIA-Studio/.local/cli-packaging-review-20260911/runtime/exact-source/project.arcadia'), '--write', '--json']",
|
||||
"JSON invocation keeps diagnostic envelope on stdout",
|
||||
"versioned JSON report agrees with process exit",
|
||||
"explicit no-op reports unchanged",
|
||||
"no-op preserves all file bytes and mtimes",
|
||||
"CLI expected exit: ['stabilize', WindowsPath('C:/Users/User/Documents/Git/ARCADIA-Studio/.local/cli-packaging-review-20260911/runtime/invalid.arcadia'), '--write', '--json']",
|
||||
"JSON invocation keeps diagnostic envelope on stdout",
|
||||
"versioned JSON report agrees with process exit",
|
||||
"invalid source write refuses with exact raw retention",
|
||||
"all packaged files and frozen ZIP unchanged after runtime checks"
|
||||
],
|
||||
"source_review": {
|
||||
"semantic_source": "01b76a1e89e93b7992f39e63560aa8b2ca2f69e0",
|
||||
"assembly_source": "a0a48e82bd43be181caca1fe2c1514c21f2bc405",
|
||||
"packaging_paths": {
|
||||
"scripts/package-artifacts.py": "24515fdbf4eebfb47b82383c6b3ccf290261cbd919f7f46a586b6488af769c8d",
|
||||
"scripts/package-cli-readme.md": "ea7bc971993e02751f24bc3cb313503a5f5fefd788540974e114398115c7c8e2",
|
||||
"scripts/package-cli-windows.ps1": "b6ec59bb55ff1e5624c782f492ce1d169131e708f257dcca195957edf7341c1a",
|
||||
"scripts/package-common.ps1": "d859453a8230b4f305d822c8742b371a71e35df698109a5d55c948f6dfaf0640",
|
||||
"scripts/package-windows.ps1": "28997ff78565619ed9c9b185f66dc922808f1f0be17f522b430c6c415448663d",
|
||||
"scripts/test-package-artifacts.py": "881718181a36fa5f7abfcd2e746bc1339446f9aeed6b2c039b8d2ce230ec202d",
|
||||
"scripts/test-package-cli-windows.ps1": "2c18f91149ede5c7b677a165bd0d8b31f3c4691642cc10dd1007a7a77bde912a",
|
||||
"scripts/test-windows-notices.ps1": "00a6ad1ed28fdb9044ceb7cc0714ceb7af095db2fb18f4828a053aa872a3147d",
|
||||
"scripts/windows-notices.ps1": "a5328c05579e1b9ca09142ee99e87c12994d12764f7c69448e392883d718ef01"
|
||||
},
|
||||
"studio_document_counts": {
|
||||
"windows": 65,
|
||||
"linux": 64
|
||||
}
|
||||
},
|
||||
"artifact": {
|
||||
"zip_sha256": "2b051f8ce1ea4f59ee7e233e1fbd516d423aadbfbca5d549755cbe78fcde1af6",
|
||||
"zip_bytes": 5091267,
|
||||
"file_count": 114,
|
||||
"files": {
|
||||
"arcadia.exe": "bad169f8be78dcfa74b32714569c886dd2132727080684fc2ba618d5822e3559",
|
||||
"dependency-notices.json": "d42920dc9958536d2d617bca17f453fc44b5e35f869b544c91abceb334cd51c1",
|
||||
"documentation-check.json": "c660725a9b9076f51c4e735c077ec149e84d9ec2db1748a129209a36b5542130",
|
||||
"libunwind.dll": "f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4",
|
||||
"LICENSE": "51009f2cb07e5d19645858b2fbc037a672a25f8cbf7999522942b86807ecf365",
|
||||
"package.json": "efe3e2227477a0f0db49ddb3a8d4ed3f48c65f9d060dd911bb3dd82e569fc446",
|
||||
"README.md": "ea7bc971993e02751f24bc3cb313503a5f5fefd788540974e114398115c7c8e2",
|
||||
"SHA256SUMS.txt": "b0b3d1c32dce6a0d5b1551e5b1c1cc9aa67d50ab2143a8645cde1dffdff6a4f9",
|
||||
"START-HERE.txt": "58e0cb3c20dcb0e94d9acf7d47f390c32e47ed55f41e2d7be775e969be4a2626",
|
||||
"THIRD-PARTY-NOTICES.txt": "8d1ffe4a7841bd8220cae95b1625e4c715d799c5dee1ba54274e9c95a3d8fb44",
|
||||
"docs/api.rst": "ca7d310138c3b4d429d80a1d03d1ac8d4a9296069e205da573034256d10438a7",
|
||||
"docs/language.html": "67dcfa3477b48a0b07298ec81741840cc3ccff12c7d58164873d746d41d611c8",
|
||||
"docs/language.rst": "ac6b1d982c55f246de2f3c844b24b874599a3c20cfbd7ed22efe4561ac622b83",
|
||||
"docs/source-cli.md": "34f12b72cc6d6ead4f47c7818c1897db592f8ad493c70776db2ab48c24d8f051",
|
||||
"licenses/llvm-mingw-LICENSE.TXT": "8d85c1057d742e597985c7d4e6320b015a9139385cff4cbae06ffc0ebe89afee",
|
||||
"examples/release1/expected-diagnostics.json": "3700393d76b940cfc076bf52861bd7b1f43370f8eabce3594531bd0bf1fc851e",
|
||||
"examples/release1/model.json": "ef0ef4006c3e09d5db6e1b621256b3f7d6642f3a7b09cc34607cfbcca830c727",
|
||||
"examples/release1/model.rs": "15f30f6bde655808fe754665c26d560e36f2a9bad340f346b09137d0d05045f5",
|
||||
"examples/release1/project.arcadia": "b2674e6bf77cfe0f6f0ba79c81d2e96d92f39c02ca2c6266d80e91723bfb9fda",
|
||||
"examples/release1/README.md": "113cfb1cf38a98b51bff60442d86a35d636a3d80ef206bece7d53d0aae67f2d5",
|
||||
"examples/release1/traceability.arcadia": "48a96f543456c9b5a5798fc795594e4119147fe45f11a6f109f141b7f514560f",
|
||||
"examples/release1/transition-before.json": "2253e6434f741687e22327c37d520ed15c043bc36025498650bc2366bb6a874d",
|
||||
"examples/release1/transition-request.json": "43b9a22e5f95ad3bc0ebd2478ead0e14406854abc989ae94ded9367b013d812a",
|
||||
"examples/text-first/project.arcadia": "f441535c623e0cf20e1875eb2cd33b587ed00e078c37af39cb36d89bb5edc19a",
|
||||
"examples/text-first/README.md": "4ec8250d5ed556f1d496e8c85137e973e802ecb11df1a36488411e8cdc5a9f9e",
|
||||
"examples/release1/evidence/repair-check.md": "f4db17bdca4ecbb1bf0caef01b2da5f1f7f798ab3152b648ce6dd8696a6a68e5",
|
||||
"examples/release1/oa/capabilities.arcadia": "fc8012e5554350b12e1cb80080f8feab80d933c9b33c0f2a7bb500cb7d609936",
|
||||
"examples/release1/oa/model.arcadia": "f7cf914766ea73ec53b85e32d117ea9a9dba52d3e038246863541ae7069b1a15",
|
||||
"examples/release1/oa/participants.arcadia": "951764c14179f27c10e315acf2ea37d1fff54c77b8441e23addd239e8c715ce3",
|
||||
"examples/release1/oa/types.arcadia": "9d0396fb82dd5733edd8d1cf52f968ca84906927c704f9cf0944a7fae215cf49",
|
||||
"examples/release1/sa/capabilities.arcadia": "b6912fb3ece439912345fa71724acd462e716a1e5a88203a1058a5c3b2c20a4f",
|
||||
"examples/release1/sa/model.arcadia": "d431509251acb859fbb98733b6d18add952495e10a78703fbe8f31e40bc083c4",
|
||||
"examples/release1/sa/participants.arcadia": "780d2eb08b217b67b55febb3d34846f727b8ee9018df6c5741748647e36b2ff8",
|
||||
"examples/release1/sa/types.arcadia": "101fc0fbd99f1101514aa7b6da4f8e535aae0bb94c9e2b9e7e6c151135b28660",
|
||||
"examples/release1/views/diagram_oa_allocation_view.arcadia": "dfc0eb61e0e2aa5bda45524571fbfa3494f44ce19696f3e15e07438074a07bc7",
|
||||
"examples/release1/views/diagram_sa_allocation_view.arcadia": "b78b43a26a82efb8fbd5374b09c329fa341f57b29f635d397b9ef8aede71d389",
|
||||
"examples/release1/views/diagram_sa_capability_view.arcadia": "75e8dc9fb30f25d6f8fd3c6b595593ab4f98958a25798f961e336e0fac557649",
|
||||
"examples/release1/views/diagram_sa_service_view.arcadia": "b08c84f3ea633e7c78dfdad3272f75415f6256cf3614b6d99bfd4f0269076c9b",
|
||||
"examples/release1/views/diagram_sa_state_view.arcadia": "5f42f4c605e57b817d8400ed21f2b14bfc4e1592aebe941ff113767504c2b869",
|
||||
"examples/release1/oa/activities/activity_oa_restore_work.arcadia": "83e73d0a013d5c3e1a4af0d9139ab0cfa362d25a846671fc2dac59181061a9d4",
|
||||
"examples/release1/oa/behavior/process_oa_assessment_procedure.arcadia": "172c32c044ef4b5bc58a1f4635cc81f48dabb4014479b47dce7b25fedf2dbd0d",
|
||||
"examples/release1/oa/behavior/process_oa_field_procedure.arcadia": "d2552dc5383e2e91cb302a790364e197f663c2edc013a2ab1978789825c9c324",
|
||||
"examples/release1/oa/behavior/process_oa_response.arcadia": "cf86f46fa5c5d70592ef43f1bc1cb4d968efd8add384ba88036bef6b88aa4abc",
|
||||
"examples/release1/oa/behavior/scenario_oa_conversation.arcadia": "b25be524e98a35ebac5e4481b7609c027b32baf9605e8097f807560f94a77ca6",
|
||||
"examples/release1/oa/behavior/scenario_oa_full_scenario.arcadia": "00a1edf2c9707a0eb62ac070c2d02467874f2c9c1f273cc6ddb53d6d65999a1c",
|
||||
"examples/release1/oa/behavior/state_machine_oa_monitor.arcadia": "447c9ad1359f4a31ecf975ad07c0b08d8e7e03191fb0858e2fc56f377b223cd5",
|
||||
"examples/release1/sa/behavior/process_sa_assessment_procedure.arcadia": "73928ced61c06cbc08728d7956676a50fd4c05aa5b7f1ff4f29ce27a0b0f2088",
|
||||
"examples/release1/sa/behavior/process_sa_field_procedure.arcadia": "886d462af83110cbfa55dd232a667ea1446761234471357cbb878794e98e8768",
|
||||
"examples/release1/sa/behavior/process_sa_response.arcadia": "041785fc22842423822431bd5e6e8d16094ebd0716965e7c765fb8c1d7549e8a",
|
||||
"examples/release1/sa/behavior/scenario_sa_conversation.arcadia": "97276f5757ee9aa12a0dbd9d3ccd58174b3091162bb8bcce357cec20a45f5d85",
|
||||
"examples/release1/sa/behavior/scenario_sa_full_scenario.arcadia": "e12fc406b99d60a2f7768aaa0b25ba560e8605f0b8e37f3a7cdd3b8443562b74",
|
||||
"examples/release1/sa/behavior/state_machine_sa_monitor.arcadia": "05ce29af99424c491c47fe2450dc6f5f8ce995511c01ec7e17bb5855be3c9736",
|
||||
"examples/release1/sa/functions/function_sa_audit_thresholds.arcadia": "e5a04325d52183956dca814dd90f4e56c96c6b7db0f4f202f203ecb49c3e0f4f",
|
||||
"examples/release1/sa/functions/function_sa_restore_work.arcadia": "fddeb67308051325041fee641fe758ddf43345b7c47b66d433616ad07fdccdd6",
|
||||
"licenses/llvm-libunwind/llvm-ea7d852a70e8-libunwind-LICENSE.TXT": "b5efebcaca80879234098e52d1725e6d9eb8fb96a19fce625d39184b705f7b6d",
|
||||
"licenses/rust/COPYING.RUNTIME": "28e85c5aa4af9b4f1dfe6b4817aa3eefb3eaaee7fd735045016f29ccf50276a1",
|
||||
"licenses/rust/COPYING3": "8ceb4b9ee5adedde47b31e975c1d90c73ad27b6b165a1dcd80c7c545eb65b903",
|
||||
"licenses/rust/COPYRIGHT-library.html": "68129500b616d5838629e68f55ff3aed5e096dacf60ce9eb41bbe599a563afa6",
|
||||
"licenses/rust/COPYRIGHT.html": "5b0c93fc4e6d4b072eaa521b1762d1e746f4c368f65e81a0e5308e2772dac85e",
|
||||
"licenses/dependencies/cfg-if-1.0.4/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
|
||||
"licenses/dependencies/cfg-if-1.0.4/LICENSE-MIT": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397",
|
||||
"licenses/dependencies/fastrand-2.5.0/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
|
||||
"licenses/dependencies/fastrand-2.5.0/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
|
||||
"licenses/dependencies/getrandom-0.4.3/LICENSE-APACHE": "aaff376532ea30a0cd5330b9502ad4a4c8bf769c539c87ffe78819d188a18ebf",
|
||||
"licenses/dependencies/getrandom-0.4.3/LICENSE-MIT": "523a42c25d245dde9c015f882cec7f4555aad883382a6cf19b4b7d9b2cd5419b",
|
||||
"licenses/dependencies/itoa-1.0.18/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
|
||||
"licenses/dependencies/itoa-1.0.18/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
|
||||
"licenses/dependencies/memchr-2.8.3/COPYING": "01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f",
|
||||
"licenses/dependencies/memchr-2.8.3/LICENSE-MIT": "0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f",
|
||||
"licenses/dependencies/memchr-2.8.3/UNLICENSE": "7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c",
|
||||
"licenses/dependencies/oa-cli-0.2.0/workspace-LICENSE": "51009f2cb07e5d19645858b2fbc037a672a25f8cbf7999522942b86807ecf365",
|
||||
"licenses/dependencies/oa-core-0.2.0/workspace-LICENSE": "51009f2cb07e5d19645858b2fbc037a672a25f8cbf7999522942b86807ecf365",
|
||||
"licenses/dependencies/once_cell-1.21.4/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
|
||||
"licenses/dependencies/once_cell-1.21.4/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
|
||||
"licenses/dependencies/proc-macro2-1.0.107/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
|
||||
"licenses/dependencies/proc-macro2-1.0.107/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
|
||||
"licenses/dependencies/quote-1.0.47/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
|
||||
"licenses/dependencies/quote-1.0.47/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
|
||||
"licenses/dependencies/rand_core-0.10.1/COPYRIGHT": "92b81db30f7ab6d693e0af5661a40d273ca1947e891123ed823924192c10cbc7",
|
||||
"licenses/dependencies/rand_core-0.10.1/LICENSE-APACHE": "6df43f6f4b5d4587f3d8d71e45532c688fd168afa5fe89d571cb32fa09c4ef51",
|
||||
"licenses/dependencies/rand_core-0.10.1/LICENSE-MIT": "8b6e9feec03e7c9a5facb26855cecd31662bf989b636bcfe79521bdf8ac863f0",
|
||||
"licenses/dependencies/same-file-1.0.6/COPYING": "01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f",
|
||||
"licenses/dependencies/same-file-1.0.6/LICENSE-MIT": "cb3c929a05e6cbc9de9ab06a4c57eeb60ca8c724bef6c138c87d3a577e27aa14",
|
||||
"licenses/dependencies/same-file-1.0.6/UNLICENSE": "7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c",
|
||||
"licenses/dependencies/serde-1.0.229/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
|
||||
"licenses/dependencies/serde-1.0.229/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
|
||||
"licenses/dependencies/serde_core-1.0.229/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
|
||||
"licenses/dependencies/serde_core-1.0.229/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
|
||||
"licenses/dependencies/serde_derive-1.0.229/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
|
||||
"licenses/dependencies/serde_derive-1.0.229/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
|
||||
"licenses/dependencies/serde_json-1.0.151/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
|
||||
"licenses/dependencies/serde_json-1.0.151/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
|
||||
"licenses/dependencies/sha1_smol-1.0.1/LICENSE": "282a21b0710441c364025461a447362daf18f5798a7b72ae2f801a2aa536da51",
|
||||
"licenses/dependencies/syn-3.0.5/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
|
||||
"licenses/dependencies/syn-3.0.5/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
|
||||
"licenses/dependencies/tempfile-3.27.0/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
|
||||
"licenses/dependencies/tempfile-3.27.0/LICENSE-MIT": "8b427f5bc501764575e52ba4f9d95673cf8f6d80a86d0d06599852e1a9a20a36",
|
||||
"licenses/dependencies/thiserror-2.0.20/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
|
||||
"licenses/dependencies/thiserror-2.0.20/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
|
||||
"licenses/dependencies/thiserror-impl-2.0.20/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
|
||||
"licenses/dependencies/thiserror-impl-2.0.20/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
|
||||
"licenses/dependencies/unicode-ident-1.0.24/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
|
||||
"licenses/dependencies/unicode-ident-1.0.24/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
|
||||
"licenses/dependencies/unicode-ident-1.0.24/LICENSE-UNICODE": "f7db81051789b729fea528a63ec4c938fdcb93d9d61d97dc8cc2e9df6d47f2a1",
|
||||
"licenses/dependencies/uuid-1.26.0/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
|
||||
"licenses/dependencies/uuid-1.26.0/LICENSE-MIT": "436bc5a105d8e57dcd8778730f3754f7bf39c14d2f530e4cde4bd2d17a83ec3d",
|
||||
"licenses/dependencies/winapi-util-0.1.11/COPYING": "01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f",
|
||||
"licenses/dependencies/winapi-util-0.1.11/LICENSE-MIT": "cb3c929a05e6cbc9de9ab06a4c57eeb60ca8c724bef6c138c87d3a577e27aa14",
|
||||
"licenses/dependencies/winapi-util-0.1.11/UNLICENSE": "7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c",
|
||||
"licenses/dependencies/windows-link-0.2.1/license-apache-2.0": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b",
|
||||
"licenses/dependencies/windows-link-0.2.1/license-mit": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383",
|
||||
"licenses/dependencies/windows-sys-0.61.2/license-apache-2.0": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b",
|
||||
"licenses/dependencies/windows-sys-0.61.2/license-mit": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383",
|
||||
"licenses/dependencies/zmij-1.0.23/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3"
|
||||
},
|
||||
"notice_packages": 27,
|
||||
"notice_reference_files": 60,
|
||||
"package_claims": {
|
||||
"product": "Arcadia CLI",
|
||||
"version": "0.2.0-preview.authoring.20260911",
|
||||
"target": "x86_64-pc-windows-gnullvm",
|
||||
"acceptance": "Packaging integrity only; clean-environment CLI verification required.",
|
||||
"build": "Prebuilt input; source/compiler/profile assertions are recorded, not inferred.",
|
||||
"build_evidence": {
|
||||
"assertion": "Caller-supplied build evidence; target and binary hashes checked, source/compiler/profile statements not inferred or independently proven by packaging.",
|
||||
"evidence": {
|
||||
"source_identity": "CLI semantic/build-input basis 01b76a1e89e93b7992f39e63560aa8b2ca2f69e0. Six original CLI manifest/workspace/lock/source/test hashes and 95 unchanged core/shared build inputs independently matched. Packaging assembly basis a0a48e82bd43be181caca1fe2c1514c21f2bc405 changes packaging only and is not claimed as the original compiler input commit.",
|
||||
"profile": "Preserved Windows debug/development artifact from cargo build --locked --offline -p oa-cli --bin arcadia, successful September11 2026 11:45:43.6035381Z,9.7336637seconds. No release-profile or reproducible-build claim.",
|
||||
"compiler": "rustc1.98.1 commit48a229ceaefd4985c50990b14116b6d856af0985,x86_64-pc-windows-gnullvm,LLVM22.1.8. Frozen executable embeds that compiler path; current toolchain target-library hashes match preserved inventory. Original compile/test/lint and independent frozen-binary review records retained separately.",
|
||||
"target": "x86_64-pc-windows-gnullvm",
|
||||
"binary_sha256": {
|
||||
"arcadia.exe": "bad169f8be78dcfa74b32714569c886dd2132727080684fc2ba618d5822e3559"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"environment": {
|
||||
"SystemRoot": "C:\\Windows",
|
||||
"WINDIR": "C:\\Windows",
|
||||
"PATH": "C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\cli-packaging-review-20260911\\extracted\\ArcadiaCLI-0.2.0-preview.authoring.20260911-x86_64-pc-windows-gnullvm;C:\\Windows\\System32;C:\\Windows",
|
||||
"TEMP": "C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\cli-packaging-review-20260911\\runtime\\temp",
|
||||
"TMP": "C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\cli-packaging-review-20260911\\runtime\\temp"
|
||||
},
|
||||
"invocations": [
|
||||
{
|
||||
"arguments": [
|
||||
"--version"
|
||||
],
|
||||
"exit": 0,
|
||||
"stdout_sha256": "0ebfaa91da207b98ef56f307b64a3f083359812b2655870f4a61459733d8590c",
|
||||
"stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
"--help"
|
||||
],
|
||||
"exit": 0,
|
||||
"stdout_sha256": "e41dd6452098023fd333c52cc210e5c2a7e08d20c377e0622deadb90d274f28f",
|
||||
"stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
"check",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\cli-packaging-review-20260911\\runtime\\examples\\text-first\\project.arcadia",
|
||||
"--json"
|
||||
],
|
||||
"exit": 0,
|
||||
"stdout_sha256": "64e6c3cb866801ec441a040406ce5111747fcb0c04a0cbfae3ab4fe0acac6fca",
|
||||
"stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
"check",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\cli-packaging-review-20260911\\runtime\\examples\\release1\\project.arcadia",
|
||||
"--json"
|
||||
],
|
||||
"exit": 0,
|
||||
"stdout_sha256": "0c29e2b2d297cfb06f0359ea1c6565997b1f396f442fc5684f12dbff8eca84bf",
|
||||
"stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
"stabilize",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\cli-packaging-review-20260911\\runtime\\exact-source\\project.arcadia",
|
||||
"--json"
|
||||
],
|
||||
"exit": 0,
|
||||
"stdout_sha256": "849cff20bd541febf6133a4206fd4dddab396857b361add78093cf01f55aeff4",
|
||||
"stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
"stabilize",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\cli-packaging-review-20260911\\runtime\\exact-source\\project.arcadia",
|
||||
"--write",
|
||||
"--json"
|
||||
],
|
||||
"exit": 0,
|
||||
"stdout_sha256": "beec44418f383d205522b6784b0aba096466e41da3b0728eb8798991149e2143",
|
||||
"stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
"rename-alias",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\cli-packaging-review-20260911\\runtime\\exact-source\\project.arcadia",
|
||||
"crew",
|
||||
"rescue_team",
|
||||
"--json"
|
||||
],
|
||||
"exit": 0,
|
||||
"stdout_sha256": "1267c7a17e2701a2dd6b91cbaacdd0e52fdb8b031660d1f788542921aa5948bb",
|
||||
"stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
"rename-alias",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\cli-packaging-review-20260911\\runtime\\exact-source\\project.arcadia",
|
||||
"crew",
|
||||
"rescue_team",
|
||||
"--write",
|
||||
"--json"
|
||||
],
|
||||
"exit": 0,
|
||||
"stdout_sha256": "0967d3dff03ebf5c5bfcfe716af8e369e23e0289885c02b83beeb4f92d5e008d",
|
||||
"stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
"stabilize",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\cli-packaging-review-20260911\\runtime\\exact-source\\project.arcadia",
|
||||
"--write",
|
||||
"--json"
|
||||
],
|
||||
"exit": 0,
|
||||
"stdout_sha256": "b95dfd2d8effc49df2a7fe893cd414a5edad9c4686c5dada4faca72ef40360b7",
|
||||
"stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
"stabilize",
|
||||
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\cli-packaging-review-20260911\\runtime\\invalid.arcadia",
|
||||
"--write",
|
||||
"--json"
|
||||
],
|
||||
"exit": 1,
|
||||
"stdout_sha256": "546ab1d5bb19dd8fa22dd6f017cc0e58b91602ccc24a048c41602b0dae45c70b",
|
||||
"stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
}
|
||||
],
|
||||
"runtime": {
|
||||
"fixed_source_ids": [
|
||||
"c073d2cf-83e5-4c98-8eaf-4a465ff0cf71",
|
||||
"1987c990-e3cb-4f0e-9be9-fe61f007b997",
|
||||
"9ba9e3cd-96f5-4f9a-865e-6b31ba94b4e6",
|
||||
"ffcf7e41-f94b-4f7c-99c1-1a30a0b46959"
|
||||
],
|
||||
"allocated_activity_id": "9ba9e3cd-96f5-4f9a-865e-6b31ba94b4e6",
|
||||
"final_source_sha256": {
|
||||
"detached.arcadia": "ae79cfc57c5e227c281f1ad8493c1cdb483700fe0fe78ec8e448135afc98d239",
|
||||
"project.arcadia": "1352c29a8cce5b5dbd7563b086680f889bdf35198f010d0083e99dd25e1007be",
|
||||
"modules/oa.arcadia": "a781e165aaddeecb05fc9110873dd1e00e7a30687cef9cded11107ee2846c162"
|
||||
},
|
||||
"source_exact_expected": {
|
||||
"detached.arcadia": "// known detached bytes remain\r\n",
|
||||
"project.arcadia": "arcadia 7;\r\nproject review \"CLI package 救助\" id \"c073d2cf-83e5-4c98-8eaf-4a465ff0cf71\" {\r\n description \"Literal @crew remains\";\r\n include \"modules/oa.arcadia\";\r\n}\r\n",
|
||||
"modules/oa.arcadia": "// @crew stays in this comment; Ω\r\noa_actor rescue_team \"Crew literal @crew\" id \"1987c990-e3cb-4f0e-9be9-fe61f007b997\" {}\r\noa_activity observe \"Observe Ω\" id \"9ba9e3cd-96f5-4f9a-865e-6b31ba94b4e6\" {}\r\nallocation responsibility \"Assigned @crew\" id \"ffcf7e41-f94b-4f7c-99c1-1a30a0b46959\" { source @rescue_team; target @observe; }\r\n"
|
||||
},
|
||||
"stabilization_note": "Separate preview UUID remains provisional; each result is checked by literal declaration role, not assumed equal across invocations."
|
||||
},
|
||||
"status": "PASS",
|
||||
"finished_utc": "2026-09-11T12:43:12Z",
|
||||
"script_sha256": "f85d1f34ab75b4067efe09886e447bf9fd54dc232020bcb1a46404ec57b2351e"
|
||||
}
|
||||
@@ -1,256 +0,0 @@
|
||||
"""Independent CLI packaging review: read-only source checks and isolated CLI runs."""
|
||||
from pathlib import Path, PurePosixPath
|
||||
import ast
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import tomllib
|
||||
import traceback
|
||||
import uuid
|
||||
import zipfile
|
||||
|
||||
OUT = Path(__file__).resolve().parent
|
||||
ROOT = OUT.parents[1]
|
||||
SOURCE = ROOT / '.local/cli-packaging-20260911/source'
|
||||
NAME = 'ArcadiaCLI-0.2.0-preview.authoring.20260911-x86_64-pc-windows-gnullvm'
|
||||
ZIP = SOURCE.parent / 'packages' / (NAME + '.zip')
|
||||
PACKAGE = OUT / 'extracted' / NAME
|
||||
BASE = '01b76a1e89e93b7992f39e63560aa8b2ca2f69e0'
|
||||
ASSEMBLY = 'a0a48e82bd43be181caca1fe2c1514c21f2bc405'
|
||||
ZIP_SHA = '2b051f8ce1ea4f59ee7e233e1fbd516d423aadbfbca5d549755cbe78fcde1af6'
|
||||
EXE_SHA = 'bad169f8be78dcfa74b32714569c886dd2132727080684fc2ba618d5822e3559'
|
||||
DLL_SHA = 'f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4'
|
||||
RESULT = {'started_utc': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()), 'checks': []}
|
||||
|
||||
|
||||
def check(ok, label):
|
||||
if not ok:
|
||||
raise AssertionError(label)
|
||||
RESULT['checks'].append(label)
|
||||
|
||||
|
||||
def sha(b):
|
||||
return hashlib.sha256(b).hexdigest()
|
||||
|
||||
|
||||
def read_json(p):
|
||||
return json.loads(p.read_text(encoding='utf-8-sig'))
|
||||
|
||||
|
||||
def record(path, obj):
|
||||
with path.open('x', encoding='utf-8', newline='\n') as f:
|
||||
json.dump(obj, f, indent=2, ensure_ascii=False)
|
||||
f.write('\n')
|
||||
|
||||
|
||||
def snapshot(directory):
|
||||
files = {}
|
||||
for p in directory.rglob('*'):
|
||||
if p.is_symlink() or getattr(p.lstat(), 'st_file_attributes', 0) & 0x400:
|
||||
raise AssertionError('unexpected reparse path ' + str(p))
|
||||
if p.is_file():
|
||||
files[p.relative_to(directory).as_posix()] = p.read_bytes()
|
||||
return files
|
||||
|
||||
|
||||
def git(path, ref=BASE):
|
||||
return subprocess.run(['git', 'show', ref + ':' + path], cwd=ROOT, check=True, capture_output=True).stdout
|
||||
|
||||
|
||||
def source_checks():
|
||||
old = git('scripts/package-artifacts.py').decode()
|
||||
new = (ROOT / 'scripts/package-artifacts.py').read_text()
|
||||
original_functions = {f.name: ast.dump(f, include_attributes=False) for f in ast.parse(old).body if isinstance(f, ast.FunctionDef)}
|
||||
new_functions = {f.name: ast.dump(f, include_attributes=False) for f in ast.parse(new).body if isinstance(f, ast.FunctionDef)}
|
||||
check(all(new_functions[k] == v for k, v in original_functions.items() if k not in ('document_inputs', 'main')), 'all original ELF/tar/document/notice helpers unchanged outside product selection')
|
||||
before, after = {'__name__': 'review_baseline'}, {'__name__': 'review_current'}
|
||||
exec(compile(old, '<baseline packaging>', 'exec'), before)
|
||||
exec(compile(new, '<current packaging>', 'exec'), after)
|
||||
counts = {}
|
||||
for platform in ('windows', 'linux'):
|
||||
expected = before['document_inputs'](SOURCE, platform)
|
||||
default = after['document_inputs'](SOURCE, platform)
|
||||
explicit = after['document_inputs'](SOURCE, platform, 'studio')
|
||||
check(default == explicit == expected, platform + ' actual full Studio document closure unchanged')
|
||||
counts[platform] = len(default['files'])
|
||||
old_notice = git('scripts/windows-notices.ps1').decode().splitlines()
|
||||
new_notice = (ROOT / 'scripts/windows-notices.ps1').read_text().splitlines()
|
||||
check(len(old_notice) == len(new_notice) and [i for i, pair in enumerate(zip(old_notice, new_notice)) if pair[0] != pair[1]] == [3], 'only notice source delta is oa-cli allowed-root check; default/body unchanged')
|
||||
for p in ('scripts/package-linux.ps1', 'deployment/Dockerfile', 'deployment/Dockerfile.dockerignore'):
|
||||
check(git(p).decode().splitlines() == (ROOT / p).read_text().splitlines(), 'Linux entry/deployment unchanged: ' + p)
|
||||
old_windows = git('scripts/package-windows.ps1').decode().replace('\r\n', '\n')
|
||||
common = (ROOT / 'scripts/package-common.ps1').read_text()
|
||||
reader_old = old_windows.split('function Read-PeImports', 1)[1].split('\n# Windows system components', 1)[0].strip()
|
||||
reader_new = common.split('function Read-PackagePeImports', 1)[1].split('\nfunction Get-PackageWindowsInputs', 1)[0].strip()
|
||||
check(reader_old == reader_new, 'PE reader extraction is byte-equivalent after function rename/EOL normalization')
|
||||
old_allowlist = re.search(r'\$systemDlls = @\((.*?)\n\)', old_windows, re.S)[1]
|
||||
new_allowlist = re.search(r'\$systemDlls = @\((.*?)\n \)', common, re.S)[1]
|
||||
check(re.findall(r"'([^']+)'", old_allowlist) == re.findall(r"'([^']+)'", new_allowlist), 'Windows system DLL allowlist unchanged')
|
||||
new_windows = (ROOT / 'scripts/package-windows.ps1').read_text()
|
||||
check("Get-PackageWindowsInputs $BinaryDirectory $Target $LlvmMinGWRoot @('arcadia-studio.exe', 'arcadia-server.exe')" in new_windows, 'Studio explicitly retains both original binary inputs')
|
||||
check(old_windows.split('$evidence = Read-PackageBuildEvidence', 1)[1].splitlines() == new_windows.split('$evidence = Read-PackageBuildEvidence', 1)[1].splitlines(), 'Studio post-inspection packaging pipeline unchanged')
|
||||
delta = subprocess.run(['git', 'diff', '--name-only', BASE, ASSEMBLY], cwd=ROOT, capture_output=True, check=True).stdout.decode().splitlines()
|
||||
check(len(delta) == 9 and all(p.startswith('scripts/') for p in delta), 'assembly delta exactly nine packaging/script paths')
|
||||
check(all((ROOT / p).read_bytes() == (SOURCE / p).read_bytes() for p in delta), 'reviewed main packaging sources equal frozen assembly bytes')
|
||||
RESULT['source_review'] = {'semantic_source': BASE, 'assembly_source': ASSEMBLY, 'packaging_paths': {p: sha((ROOT / p).read_bytes()) for p in delta}, 'studio_document_counts': counts}
|
||||
|
||||
|
||||
def artifact():
|
||||
check(not PACKAGE.parent.exists(), 'fresh extraction directory')
|
||||
check(ZIP.stat().st_size == 5091267 and sha(ZIP.read_bytes()) == ZIP_SHA, 'frozen ZIP size and hash')
|
||||
with zipfile.ZipFile(ZIP) as z:
|
||||
names = z.namelist()
|
||||
check(len(names) == 114 and len({n.casefold() for n in names}) == 114, '114 unique ZIP files')
|
||||
check(all(n.startswith(NAME + '/') and PurePosixPath(n).as_posix() == n and '..' not in PurePosixPath(n).parts and '\\' not in n and ':' not in n and all(not p.endswith((' ', '.')) for p in PurePosixPath(n).parts) for n in names), 'single safe normalized Windows ZIP root')
|
||||
check(all(not i.is_dir() and stat.S_IFMT(i.external_attr >> 16) in (0, stat.S_IFREG) and i.date_time == (2000, 1, 1, 0, 0, 0) for i in z.infolist()) and z.testzip() is None, 'ordinary fixed-time ZIP members and CRCs')
|
||||
PACKAGE.parent.mkdir()
|
||||
z.extractall(PACKAGE.parent)
|
||||
archived = {n[len(NAME) + 1:]: z.read(n) for n in names}
|
||||
actual = snapshot(PACKAGE)
|
||||
check(actual == archived, 'complete clean extraction equals ZIP bytes')
|
||||
hashes = {p: sha(b) for p, b in actual.items()}
|
||||
lines = actual['SHA256SUMS.txt'].decode().splitlines()
|
||||
sums = dict((line[66:], line[:64]) for line in lines)
|
||||
check(len(sums) == len(lines) and sums == {p: h for p, h in hashes.items() if p != 'SHA256SUMS.txt'}, 'complete checksum inventory equals every other extracted file')
|
||||
package = read_json(PACKAGE / 'package.json')
|
||||
generated = {'SHA256SUMS.txt', 'START-HERE.txt', 'THIRD-PARTY-NOTICES.txt', 'dependency-notices.json', 'documentation-check.json', 'package.json'}
|
||||
check(set(actual) - set(package['input_sha256']) == generated and all(hashes[p] == h for p, h in package['input_sha256'].items()), 'all package inputs plus exactly six generated records')
|
||||
check(hashes['arcadia.exe'] == EXE_SHA and hashes['libunwind.dll'] == DLL_SHA, 'frozen CLI and runtime hashes')
|
||||
check(not {'arcadia-studio.exe', 'arcadia-server.exe'} & set(actual), 'no unrelated executable payload')
|
||||
for p in actual:
|
||||
if p.startswith(('docs/', 'examples/')) or p == 'LICENSE':
|
||||
check((SOURCE / p).read_bytes() == actual[p], 'exact public document/example: ' + p)
|
||||
check(actual['README.md'] == (SOURCE / 'scripts/package-cli-readme.md').read_bytes(), 'CLI-specific README')
|
||||
notice = read_json(PACKAGE / 'dependency-notices.json')
|
||||
check(notice['root_packages'] == ['oa-cli'] and len(notice['packages']) == 27, '27 dependency packages; only CLI root')
|
||||
check(notice['cargo_lock_sha256'] == sha((SOURCE / 'Cargo.lock').read_bytes()) == '2b960242c33eb839ff008d01731f6d5cdb4e047ca8cd6e13151cbf9c09913510', 'original CLI Cargo lock identity')
|
||||
lock = {(p['name'], p['version'], p.get('source')): p for p in tomllib.loads((SOURCE / 'Cargo.lock').read_text())['package']}
|
||||
references = []
|
||||
for p in notice['packages']:
|
||||
check(lock[p['name'], p['version'], p['source']].get('checksum') == p['registry_checksum'], 'locked dependency: ' + p['name'])
|
||||
references.extend(p['notices'])
|
||||
for f in p['embedded_fonts']:
|
||||
references.extend(f['notices'])
|
||||
if p['source'] is None:
|
||||
local = snapshot(SOURCE / 'crates' / p['name'])
|
||||
local = {k: sha(v) for k, v in local.items() if 'target' not in PurePosixPath(k).parts and PurePosixPath(k).name != '.cargo-ok'}
|
||||
check(local == p['local_source_sha256'], 'complete local crate inputs: ' + p['name'])
|
||||
references += notice['rust']['notices'] + notice['runtime']['notices']
|
||||
check(all(hashes[i['path']] == i['sha256'] for i in references), 'all package/compiler/runtime notice references and bytes')
|
||||
check(notice['runtime']['sha256'] == DLL_SHA, 'runtime notice binds packaged DLL')
|
||||
check(all(sha((SOURCE / p).read_bytes()) == h for p, h in notice['workspace_build_inputs_sha256'].items()), 'all shared build input hashes')
|
||||
original = read_json(ROOT / '.local/source-cli-20260911/verified/manifest.json')
|
||||
cli_inputs = [i for i in original['Artifacts'] if not i['Path'].startswith('.local/')]
|
||||
check(len(cli_inputs) == 6 and all(sha((SOURCE / i['Path']).read_bytes()) == i['SHA256'].lower() for i in cli_inputs), 'six original CLI manifest inputs')
|
||||
core_inputs = [i for i in read_json(ROOT / '.local/behavior-creation-20260911/gui-verified/manifest.json')['inputs'] if i['path'].startswith(('crates/oa-core/', 'build/'))]
|
||||
check(len(core_inputs) == 95 and all(sha((SOURCE / i['path']).read_bytes()) == i['sha256'].lower() for i in core_inputs), '95 original core/shared build inputs')
|
||||
RESULT['artifact'] = {'zip_sha256': ZIP_SHA, 'zip_bytes': ZIP.stat().st_size, 'file_count': len(actual), 'files': hashes, 'notice_packages': 27, 'notice_reference_files': len({i['path'] for i in references}), 'package_claims': {k: package[k] for k in ('product', 'version', 'target', 'acceptance', 'build', 'build_evidence')}}
|
||||
return actual
|
||||
|
||||
|
||||
def runtime(original_package):
|
||||
run = OUT / 'runtime'
|
||||
run.mkdir()
|
||||
temp = run / 'temp'
|
||||
temp.mkdir()
|
||||
windows = Path(os.environ['SystemRoot'])
|
||||
env = {'SystemRoot': str(windows), 'WINDIR': str(windows), 'PATH': ';'.join(map(str, (PACKAGE, windows / 'System32', windows))), 'TEMP': str(temp), 'TMP': str(temp)}
|
||||
RESULT['environment'] = env
|
||||
RESULT['invocations'] = []
|
||||
def invoke(args, expect=0, report=True):
|
||||
index = len(RESULT['invocations']) + 1
|
||||
r = subprocess.run([str(PACKAGE / 'arcadia.exe'), *map(str, args)], cwd=run, env=env, capture_output=True, timeout=30, creationflags=subprocess.CREATE_NO_WINDOW)
|
||||
(run / f'{index:02d}.stdout').write_bytes(r.stdout)
|
||||
(run / f'{index:02d}.stderr').write_bytes(r.stderr)
|
||||
RESULT['invocations'].append({'arguments': list(map(str, args)), 'exit': r.returncode, 'stdout_sha256': sha(r.stdout), 'stderr_sha256': sha(r.stderr)})
|
||||
check(r.returncode == expect, 'CLI expected exit: ' + str(args))
|
||||
if not report:
|
||||
return r.stdout
|
||||
check(not r.stderr, 'JSON invocation keeps diagnostic envelope on stdout')
|
||||
value = json.loads(r.stdout)
|
||||
check(value['format_version'] == 1 and value['exit_code'] == expect, 'versioned JSON report agrees with process exit')
|
||||
return value
|
||||
check(invoke(['--version'], report=False).decode().strip() == 'arcadia 0.2.0', 'underlying CLI version')
|
||||
help_text = invoke(['--help'], report=False).decode()
|
||||
check(all(word in help_text for word in ('check ROOT', 'stabilize ROOT', 'rename-alias ROOT OLD NEW', '--write', 'provisional')), 'packaged help has command/preview contract')
|
||||
examples = run / 'examples'
|
||||
shutil.copytree(PACKAGE / 'examples', examples)
|
||||
before_examples = snapshot(examples)
|
||||
for relative in ('text-first/project.arcadia', 'release1/project.arcadia'):
|
||||
r = invoke(['check', examples / relative, '--json'])
|
||||
check(r['status'] == 'checked' and not r['written'] and not r['changes'], 'packaged example check: ' + relative)
|
||||
check(snapshot(examples) == before_examples, 'checks preserve complete copied examples byte-for-byte')
|
||||
fixture = run / 'exact-source'
|
||||
(fixture / 'modules').mkdir(parents=True)
|
||||
entry = 'arcadia 7;\r\nproject review "CLI package 救助" id "c073d2cf-83e5-4c98-8eaf-4a465ff0cf71" {\r\n description "Literal @crew remains";\r\n include "modules/oa.arcadia";\r\n}\r\n'
|
||||
module = '// @crew stays in this comment; Ω\r\noa_actor crew "Crew literal @crew" id "1987c990-e3cb-4f0e-9be9-fe61f007b997" {}\r\noa_activity observe "Observe Ω" {}\r\nallocation responsibility "Assigned @crew" id "ffcf7e41-f94b-4f7c-99c1-1a30a0b46959" { source @crew; target @observe; }\r\n'
|
||||
(fixture / 'project.arcadia').write_bytes(entry.encode())
|
||||
(fixture / 'modules/oa.arcadia').write_bytes(module.encode())
|
||||
(fixture / 'detached.arcadia').write_bytes(b'// known detached bytes remain\r\n')
|
||||
before = snapshot(fixture)
|
||||
root = fixture / 'project.arcadia'
|
||||
def stabilized(report):
|
||||
check(len(report['changes']) == 1 and report['changes'][0]['path'] == 'modules/oa.arcadia' and report['changes'][0]['before'] == module, 'only missing-ID source changes; exact captured raw bytes')
|
||||
text = report['changes'][0]['after']
|
||||
match = re.search(r'oa_activity observe "Observe Ω" id "([0-9a-f-]{36})"', text)
|
||||
check(match is not None, 'fresh ID is attached to literal intended activity')
|
||||
identity = str(uuid.UUID(match[1]))
|
||||
check(identity not in (entry + module), 'allocated identity differs from every preexisting identity')
|
||||
expected = module.replace('oa_activity observe "Observe Ω" {}', f'oa_activity observe "Observe Ω" id "{identity}" {{}}')
|
||||
check(text == expected, 'stabilization preserves every other character, CRLF, comment, name and reference')
|
||||
return text, identity
|
||||
preview = invoke(['stabilize', root, '--json'])
|
||||
check(preview['status'] == 'preview' and not preview['written'] and preview['identities_to_stabilize'] == 1, 'stabilization is preview by default')
|
||||
stabilized(preview)
|
||||
check(snapshot(fixture) == before, 'preview preserves all physical source bytes including detached file')
|
||||
written = invoke(['stabilize', root, '--write', '--json'])
|
||||
check(written['status'] == 'written' and written['written'], 'explicit stabilization writes')
|
||||
text, identity = stabilized(written)
|
||||
stable_expected = dict(before, **{'modules/oa.arcadia': text.encode()})
|
||||
check(snapshot(fixture) == stable_expected, 'stabilized complete physical file inventory and bytes')
|
||||
renamed = text.replace('oa_actor crew ', 'oa_actor rescue_team ').replace('source @crew;', 'source @rescue_team;')
|
||||
preview = invoke(['rename-alias', root, 'crew', 'rescue_team', '--json'])
|
||||
check(not preview['written'] and preview['status'] == 'preview' and [(c['path'], c['before'], c['after']) for c in preview['changes']] == [('modules/oa.arcadia', text, renamed)], 'alias preview changes exact declaration and typed reference only')
|
||||
check(snapshot(fixture) == stable_expected, 'rename preview retains exact source bytes and identities')
|
||||
saved = invoke(['rename-alias', root, 'crew', 'rescue_team', '--write', '--json'])
|
||||
renamed_expected = dict(before, **{'modules/oa.arcadia': renamed.encode()})
|
||||
check(saved['written'] and snapshot(fixture) == renamed_expected, 'explicit alias write matches full independent raw expected files')
|
||||
expected_ids = re.findall(r'\bid "([0-9a-f-]{36})"', entry + text)
|
||||
check(re.findall(r'\bid "([0-9a-f-]{36})"', entry + renamed) == expected_ids and len(expected_ids) == 4, 'all four identities remain ordered and exact through rename')
|
||||
timestamps = {p: (fixture / p).stat().st_mtime_ns for p in renamed_expected}
|
||||
noop = invoke(['stabilize', root, '--write', '--json'])
|
||||
check(noop['status'] == 'unchanged' and not noop['written'] and not noop['changes'] and noop['identities_to_stabilize'] == 0, 'explicit no-op reports unchanged')
|
||||
check(snapshot(fixture) == renamed_expected and timestamps == {p: (fixture / p).stat().st_mtime_ns for p in renamed_expected}, 'no-op preserves all file bytes and mtimes')
|
||||
invalid = run / 'invalid.arcadia'
|
||||
invalid.write_bytes(b'arcadia 7;\r\nproject broken "Unclosed" {\r\n')
|
||||
invalid_before = invalid.read_bytes()
|
||||
refusal = invoke(['stabilize', invalid, '--write', '--json'], expect=1)
|
||||
check(not refusal['written'] and invalid.read_bytes() == invalid_before, 'invalid source write refuses with exact raw retention')
|
||||
check(snapshot(PACKAGE) == original_package and sha(ZIP.read_bytes()) == ZIP_SHA, 'all packaged files and frozen ZIP unchanged after runtime checks')
|
||||
RESULT['runtime'] = {'fixed_source_ids': expected_ids, 'allocated_activity_id': identity, 'final_source_sha256': {p: sha(b) for p, b in renamed_expected.items()}, 'source_exact_expected': {p: b.decode() for p, b in renamed_expected.items()}, 'stabilization_note': 'Separate preview UUID remains provisional; each result is checked by literal declaration role, not assumed equal across invocations.'}
|
||||
|
||||
|
||||
try:
|
||||
check(not (OUT / 'result.json').exists(), 'preserve earlier evidence; no overwrite')
|
||||
source_checks()
|
||||
frozen = artifact()
|
||||
runtime(frozen)
|
||||
RESULT['status'] = 'PASS'
|
||||
except Exception as error:
|
||||
RESULT['status'] = 'FAIL'
|
||||
RESULT['error'] = repr(error)
|
||||
RESULT['traceback'] = traceback.format_exc()
|
||||
RESULT['finished_utc'] = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
|
||||
RESULT['script_sha256'] = sha(Path(__file__).read_bytes())
|
||||
record(OUT / 'result.json', RESULT)
|
||||
print(json.dumps({k: RESULT[k] for k in ('status', 'started_utc', 'finished_utc')}, indent=2))
|
||||
print('checks:', len(RESULT['checks']), 'invocations:', len(RESULT.get('invocations', [])))
|
||||
if RESULT['status'] == 'FAIL':
|
||||
print(RESULT['traceback'])
|
||||
sys.exit(1)
|
||||
@@ -1 +0,0 @@
|
||||
PASS: 15 CLI/Windows packaging assertions; inert fixture retained at C:\Temp\arcadia-cli-package-2a5110e4fc074b6f9db9bfa623db315c
|
||||
@@ -1,112 +0,0 @@
|
||||
arcadia 7;
|
||||
project alarm_suite "Alarm response" id "a0f5c4c2-f39c-4095-9816-71f1862e24d8" {
|
||||
description "A complete small OA/SA source-authoring example. Numerical annotations are descriptive. Literal aliases: @alarm_response; @maintenance; @alarm_message; @response_view; @detector_card; @response_procedure; @assessment_refinement;";
|
||||
|
||||
oa_actor plant_operator "Plant operator" id "60539571-2921-4e7d-ab83-bae670d0cb15" {}
|
||||
oa_entity maintenance_staff "Maintenance team" id "6af96577-1b04-4ba1-b91b-6cef61adc6d6" {}
|
||||
oa_activity manage_alarm "Manage a pressure alarm" id "4154bf39-9fd5-490e-aebf-fba3d67b697c" {
|
||||
oa_activity detect_alarm "Detect alarm" id "bf9e9d8a-edb2-47f4-93f9-7ae373c42160" {}
|
||||
// Assessing the alarm is work decomposition, independently allocated below.
|
||||
oa_activity assess_alarm "Assess alarm" id "8cbd5a4f-1c31-49c9-8a01-987144ce962e" {}
|
||||
}
|
||||
oa_capability safe_response "Respond safely" id "c097b1b5-45dc-49de-bcf6-89b5ccb752d1" {}
|
||||
oa_exchange_item alarm_report "Alarm report" id "0a48a460-964a-4578-97c4-428707e8b201" {
|
||||
mechanism Flow;
|
||||
oa_data_field reported_pressure "Reported pressure" id "0bf7f442-cc8d-4782-ba65-f357e91fd477" {
|
||||
type_ref Real;
|
||||
unit "kPa";
|
||||
}
|
||||
}
|
||||
allocation operator_detects "Operator detects" id "8653fb7d-529d-495d-abde-897dee36b4bb" {
|
||||
source @plant_operator; target @detect_alarm;
|
||||
}
|
||||
allocation team_assesses "Team assesses" id "bb36afbf-9878-401b-b172-d04b08ecf603" {
|
||||
source @maintenance_staff; target @assess_alarm;
|
||||
}
|
||||
interaction alarm_payload "Pressure alarm report" id "269e5bdd-2478-4a43-9c77-84d99ac4e8a6" {
|
||||
source @detect_alarm; target @assess_alarm; items [@alarm_report];
|
||||
}
|
||||
capability_involvement assessment_supports_response "Assessment supports safe response" id "27633a6d-d962-4d19-9367-9ca3f00de0b0" {
|
||||
source @assess_alarm; target @safe_response;
|
||||
}
|
||||
|
||||
oa_process triage "Alarm triage" id "9888d781-09f3-4861-b9dc-7a97080d24d6" {
|
||||
steps [@detect_once, @assess_once];
|
||||
oa_occurrence detect_once "Detect this alarm" id "6ca87cbe-9579-49b3-884e-e6ee63ba8c5b" {
|
||||
body { Action { action @detect_alarm; lifeline none; }; };
|
||||
}
|
||||
oa_occurrence assess_once "Assess this alarm" id "528d1c78-62d9-4e45-bb4a-cf529d8ed4a9" {
|
||||
body { Action { action @assess_alarm; lifeline none; }; };
|
||||
}
|
||||
}
|
||||
sequence_link assessment_after_detection "Assess after detecting" id "bfd9c0e7-832b-4983-90aa-fc37d23193e2" {
|
||||
source @detect_once; target @assess_once;
|
||||
}
|
||||
chain_involvement triage_supports_response "Triage supports safe response" id "2aeba184-bf69-49c7-87ff-cc2da82b006c" {
|
||||
source @triage; target @safe_response;
|
||||
}
|
||||
|
||||
oa_scenario notification "Alarm notification" id "69e23f04-7bb3-4db1-a2a3-d8e65101c546" {
|
||||
viewpoint OperationalActivity;
|
||||
context @safe_response;
|
||||
steps [@notify_once];
|
||||
oa_lifeline detector_line "Detector" id "57c62024-6d19-4818-ac8c-66fb4a9b3da7" { represented @detect_alarm; }
|
||||
oa_lifeline assessor_line "Assessor" id "1b421446-9d17-4c8b-90f1-6fbe8b513a04" { represented @assess_alarm; }
|
||||
oa_occurrence notify_once "Report this pressure" id "a1c37b31-41ed-4e16-a243-a50702ff1bc1" {
|
||||
body { Exchange {
|
||||
exchange @alarm_payload; sender @detector_line; receiver @assessor_line;
|
||||
message_kind Asynchronous; reply_to none;
|
||||
values [{ field @reported_pressure; path []; expression "420"; }];
|
||||
}; };
|
||||
}
|
||||
}
|
||||
|
||||
sa_system alarm_console "Alarm console" id "9ea585b5-be12-4204-8909-02ea9bed49f0" {
|
||||
sa_state_machine console_status "Console status" id "a6c6f463-5c80-4b02-a2e6-67cd0088b2f2" {
|
||||
sa_region console_region "Main region" id "e8b62edc-2233-4ec4-a811-2e8baf81a6e9" {
|
||||
sa_initial console_start "Start" id "5a92c3a8-6af5-452a-849e-c2a17dc33260" {}
|
||||
sa_state console_ready "Ready" id "9064a197-96ea-4bdf-9dd6-2e9ef66856d8" {}
|
||||
sa_state console_alerting "Alerting" id "4d167878-2bbe-4acd-abd6-ee7dcd42ec50" {}
|
||||
}
|
||||
}
|
||||
}
|
||||
sa_function classify_alarm "Classify alarm" id "ad6941ee-f900-4a2b-a368-d8879f1ffece" {}
|
||||
sa_functional_chain classification_flow "Alarm classification" id "578dbcd5-3c28-43d8-913e-af27bc51ebde" {
|
||||
steps [@classify_once];
|
||||
sa_occurrence classify_once "Classify this alarm" id "a6c37863-8e72-44f9-a8a3-fea06341c714" {
|
||||
body { Action { action @classify_alarm; lifeline none; }; };
|
||||
}
|
||||
}
|
||||
allocation console_classifies "Console classifies" id "294d4262-303f-41db-b01e-fe47c1647693" {
|
||||
source @alarm_console; target @classify_alarm;
|
||||
}
|
||||
sa_event alarm_received "Alarm received" id "7fba860a-721f-4da2-8315-010de2e9c69e" {}
|
||||
state_transition initialize_console "Initialize" id "839d4359-c78f-414b-867e-5b40b1f145cc" {
|
||||
source @console_start; target @console_ready;
|
||||
}
|
||||
state_transition display_alarm "Display alarm" id "dd383165-1b89-46b9-8ae3-80a6b6406a4e" {
|
||||
source @console_ready; target @console_alerting; triggers [@alarm_received];
|
||||
}
|
||||
trace assessment_alignment "Alarm classification refines assessment" id "34142333-67c2-4972-a64f-47e140ed93ca" {
|
||||
oa @assess_alarm; sa @classify_alarm;
|
||||
baseline {
|
||||
oa { name "Assess alarm"; description ""; };
|
||||
sa { name "Classify alarm"; description ""; };
|
||||
};
|
||||
}
|
||||
artifact response_handbook "Response procedure" id "6ba54037-c6f1-495b-ba96-6f2e910a218c" {
|
||||
owner @assess_alarm; uri "procedures/alarm-response.md"; revision "review-1";
|
||||
}
|
||||
|
||||
diagram response_map "Operational work" id "a44d9d69-78a0-49de-8eb8-3757aa6c0919" { projection FunctionalDataflow; }
|
||||
node detector_tile id "45f016f4-3558-45b9-9559-da8ef9d5dcad" {
|
||||
diagram @response_map; element @detect_alarm;
|
||||
position { x 40; y 40; };
|
||||
}
|
||||
node assessor_card id "6ee7a0cc-013b-4a0f-8cff-513872e028b7" {
|
||||
diagram @response_map; element @assess_alarm;
|
||||
position { x 400; y 40; };
|
||||
}
|
||||
}
|
||||
|
||||
// Retain these comment references: @alarm_response; @maintenance; @alarm_message; @response_view; @detector_card; @response_procedure; @assessment_refinement;
|
||||
@@ -1,112 +0,0 @@
|
||||
arcadia 7;
|
||||
project alarm_response "Alarm response" id "a0f5c4c2-f39c-4095-9816-71f1862e24d8" {
|
||||
description "A complete small OA/SA source-authoring example. Numerical annotations are descriptive. Literal aliases: @alarm_response; @maintenance; @alarm_message; @response_view; @detector_card; @response_procedure; @assessment_refinement;";
|
||||
|
||||
oa_actor plant_operator "Plant operator" id "60539571-2921-4e7d-ab83-bae670d0cb15" {}
|
||||
oa_entity maintenance "Maintenance team" id "6af96577-1b04-4ba1-b91b-6cef61adc6d6" {}
|
||||
oa_activity manage_alarm "Manage a pressure alarm" id "4154bf39-9fd5-490e-aebf-fba3d67b697c" {
|
||||
oa_activity detect_alarm "Detect alarm" id "bf9e9d8a-edb2-47f4-93f9-7ae373c42160" {}
|
||||
// Assessing the alarm is work decomposition, independently allocated below.
|
||||
oa_activity assess_alarm "Assess alarm" id "8cbd5a4f-1c31-49c9-8a01-987144ce962e" {}
|
||||
}
|
||||
oa_capability safe_response "Respond safely" id "c097b1b5-45dc-49de-bcf6-89b5ccb752d1" {}
|
||||
oa_exchange_item alarm_report "Alarm report" id "0a48a460-964a-4578-97c4-428707e8b201" {
|
||||
mechanism Flow;
|
||||
oa_data_field reported_pressure "Reported pressure" id "0bf7f442-cc8d-4782-ba65-f357e91fd477" {
|
||||
type_ref Real;
|
||||
unit "kPa";
|
||||
}
|
||||
}
|
||||
allocation operator_detects "Operator detects" id "8653fb7d-529d-495d-abde-897dee36b4bb" {
|
||||
source @plant_operator; target @detect_alarm;
|
||||
}
|
||||
allocation team_assesses "Team assesses" id "bb36afbf-9878-401b-b172-d04b08ecf603" {
|
||||
source @maintenance; target @assess_alarm;
|
||||
}
|
||||
interaction alarm_message "Pressure alarm report" id "269e5bdd-2478-4a43-9c77-84d99ac4e8a6" {
|
||||
source @detect_alarm; target @assess_alarm; items [@alarm_report];
|
||||
}
|
||||
capability_involvement assessment_supports_response "Assessment supports safe response" id "27633a6d-d962-4d19-9367-9ca3f00de0b0" {
|
||||
source @assess_alarm; target @safe_response;
|
||||
}
|
||||
|
||||
oa_process triage "Alarm triage" id "9888d781-09f3-4861-b9dc-7a97080d24d6" {
|
||||
steps [@detect_once, @assess_once];
|
||||
oa_occurrence detect_once "Detect this alarm" id "6ca87cbe-9579-49b3-884e-e6ee63ba8c5b" {
|
||||
body { Action { action @detect_alarm; lifeline none; }; };
|
||||
}
|
||||
oa_occurrence assess_once "Assess this alarm" id "528d1c78-62d9-4e45-bb4a-cf529d8ed4a9" {
|
||||
body { Action { action @assess_alarm; lifeline none; }; };
|
||||
}
|
||||
}
|
||||
sequence_link assessment_after_detection "Assess after detecting" id "bfd9c0e7-832b-4983-90aa-fc37d23193e2" {
|
||||
source @detect_once; target @assess_once;
|
||||
}
|
||||
chain_involvement triage_supports_response "Triage supports safe response" id "2aeba184-bf69-49c7-87ff-cc2da82b006c" {
|
||||
source @triage; target @safe_response;
|
||||
}
|
||||
|
||||
oa_scenario notification "Alarm notification" id "69e23f04-7bb3-4db1-a2a3-d8e65101c546" {
|
||||
viewpoint OperationalActivity;
|
||||
context @safe_response;
|
||||
steps [@notify_once];
|
||||
oa_lifeline detector_line "Detector" id "57c62024-6d19-4818-ac8c-66fb4a9b3da7" { represented @detect_alarm; }
|
||||
oa_lifeline assessor_line "Assessor" id "1b421446-9d17-4c8b-90f1-6fbe8b513a04" { represented @assess_alarm; }
|
||||
oa_occurrence notify_once "Report this pressure" id "a1c37b31-41ed-4e16-a243-a50702ff1bc1" {
|
||||
body { Exchange {
|
||||
exchange @alarm_message; sender @detector_line; receiver @assessor_line;
|
||||
message_kind Asynchronous; reply_to none;
|
||||
values [{ field @reported_pressure; path []; expression "420"; }];
|
||||
}; };
|
||||
}
|
||||
}
|
||||
|
||||
sa_system alarm_console "Alarm console" id "9ea585b5-be12-4204-8909-02ea9bed49f0" {
|
||||
sa_state_machine console_status "Console status" id "a6c6f463-5c80-4b02-a2e6-67cd0088b2f2" {
|
||||
sa_region console_region "Main region" id "e8b62edc-2233-4ec4-a811-2e8baf81a6e9" {
|
||||
sa_initial console_start "Start" id "5a92c3a8-6af5-452a-849e-c2a17dc33260" {}
|
||||
sa_state console_ready "Ready" id "9064a197-96ea-4bdf-9dd6-2e9ef66856d8" {}
|
||||
sa_state console_alerting "Alerting" id "4d167878-2bbe-4acd-abd6-ee7dcd42ec50" {}
|
||||
}
|
||||
}
|
||||
}
|
||||
sa_function classify_alarm "Classify alarm" id "ad6941ee-f900-4a2b-a368-d8879f1ffece" {}
|
||||
sa_functional_chain classification_flow "Alarm classification" id "578dbcd5-3c28-43d8-913e-af27bc51ebde" {
|
||||
steps [@classify_once];
|
||||
sa_occurrence classify_once "Classify this alarm" id "a6c37863-8e72-44f9-a8a3-fea06341c714" {
|
||||
body { Action { action @classify_alarm; lifeline none; }; };
|
||||
}
|
||||
}
|
||||
allocation console_classifies "Console classifies" id "294d4262-303f-41db-b01e-fe47c1647693" {
|
||||
source @alarm_console; target @classify_alarm;
|
||||
}
|
||||
sa_event alarm_received "Alarm received" id "7fba860a-721f-4da2-8315-010de2e9c69e" {}
|
||||
state_transition initialize_console "Initialize" id "839d4359-c78f-414b-867e-5b40b1f145cc" {
|
||||
source @console_start; target @console_ready;
|
||||
}
|
||||
state_transition display_alarm "Display alarm" id "dd383165-1b89-46b9-8ae3-80a6b6406a4e" {
|
||||
source @console_ready; target @console_alerting; triggers [@alarm_received];
|
||||
}
|
||||
trace assessment_refinement "Alarm classification refines assessment" id "34142333-67c2-4972-a64f-47e140ed93ca" {
|
||||
oa @assess_alarm; sa @classify_alarm;
|
||||
baseline {
|
||||
oa { name "Assess alarm"; description ""; };
|
||||
sa { name "Classify alarm"; description ""; };
|
||||
};
|
||||
}
|
||||
artifact response_procedure "Response procedure" id "6ba54037-c6f1-495b-ba96-6f2e910a218c" {
|
||||
owner @assess_alarm; uri "procedures/alarm-response.md"; revision "review-1";
|
||||
}
|
||||
|
||||
diagram response_view "Operational work" id "a44d9d69-78a0-49de-8eb8-3757aa6c0919" { projection FunctionalDataflow; }
|
||||
node detector_card id "45f016f4-3558-45b9-9559-da8ef9d5dcad" {
|
||||
diagram @response_view; element @detect_alarm;
|
||||
position { x 40; y 40; };
|
||||
}
|
||||
node assessor_card id "6ee7a0cc-013b-4a0f-8cff-513872e028b7" {
|
||||
diagram @response_view; element @assess_alarm;
|
||||
position { x 400; y 40; };
|
||||
}
|
||||
}
|
||||
|
||||
// Retain these comment references: @alarm_response; @maintenance; @alarm_message; @response_view; @detector_card; @response_procedure; @assessment_refinement;
|
||||
@@ -1,200 +0,0 @@
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$Repository,
|
||||
[ValidatePattern('^[a-z0-9-]+$')][string]$RunName = 'verified'
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Set-StrictMode -Version Latest
|
||||
$repoRoot = [IO.Path]::GetFullPath($Repository)
|
||||
$utf8 = [Text.UTF8Encoding]::new($false, $true)
|
||||
$exe = Join-Path $repoRoot '.local/source-cli-20260911/verified/arcadia.exe'
|
||||
$expectedExeHash = 'BAD169F8BE78DCFA74B32714569C886DD2132727080684FC2BA618D5822E3559'
|
||||
$runRoot = Join-Path $repoRoot ('.local/source-cli-independent-20260911/' + $RunName)
|
||||
if (Test-Path -LiteralPath $runRoot) { throw "Refusing to replace prior evidence: $runRoot" }
|
||||
$checks = [Collections.Generic.List[string]]::new()
|
||||
$operations = [Collections.Generic.List[object]]::new()
|
||||
function Assert-That([bool]$condition, [string]$label) {
|
||||
if (!$condition) { throw "Independent contract failed: $label" }
|
||||
$checks.Add($label)
|
||||
}
|
||||
function Put([string]$path, [string]$value) {
|
||||
[IO.Directory]::CreateDirectory([IO.Path]::GetDirectoryName($path)) | Out-Null
|
||||
[IO.File]::WriteAllText($path, $value, $utf8)
|
||||
}
|
||||
function Read-Exact([string]$path) { [IO.File]::ReadAllText($path, $utf8) }
|
||||
function Text-Hash([string]$text) {
|
||||
[Convert]::ToHexString([Security.Cryptography.SHA256]::HashData($utf8.GetBytes($text)))
|
||||
}
|
||||
function Invoke-ArcadiaProbe([string[]]$words) {
|
||||
$info = [Diagnostics.ProcessStartInfo]::new($exe)
|
||||
$info.UseShellExecute = $false
|
||||
$info.CreateNoWindow = $true
|
||||
$info.RedirectStandardOutput = $true
|
||||
$info.RedirectStandardError = $true
|
||||
$info.StandardOutputEncoding = $utf8
|
||||
$info.StandardErrorEncoding = $utf8
|
||||
foreach ($word in $words) { $info.ArgumentList.Add($word) }
|
||||
$info.ArgumentList.Add('--json')
|
||||
$process = [Diagnostics.Process]::new()
|
||||
$process.StartInfo = $info
|
||||
$process.Start() | Out-Null
|
||||
$output = $process.StandardOutput.ReadToEndAsync()
|
||||
$errorText = $process.StandardError.ReadToEndAsync()
|
||||
if (!$process.WaitForExit(60000)) {
|
||||
$process.Kill($true)
|
||||
throw 'CLI probe exceeded 60 seconds'
|
||||
}
|
||||
$raw = $output.GetAwaiter().GetResult()
|
||||
$err = $errorText.GetAwaiter().GetResult()
|
||||
$report = $raw | ConvertFrom-Json -Depth 100
|
||||
Assert-That ($process.ExitCode -eq $report.exit_code) 'process exit agrees with JSON'
|
||||
Assert-That ($err -ceq '') 'JSON invocation emits no mixed stderr'
|
||||
$operations.Add([ordered]@{
|
||||
Arguments = @($words | ForEach-Object { $_.Replace($runRoot, '<fixture>') })
|
||||
Exit = $process.ExitCode
|
||||
Status = $report.status
|
||||
Changes = @($report.changes).Count
|
||||
Diagnostics = @($report.diagnostics).Count
|
||||
StdoutSHA256 = Text-Hash $raw
|
||||
})
|
||||
$process.Dispose()
|
||||
return $report
|
||||
}
|
||||
Assert-That ((Get-FileHash -LiteralPath $exe).Hash -ceq $expectedExeHash) 'immutable CLI binary pin'
|
||||
[IO.Directory]::CreateDirectory($runRoot) | Out-Null
|
||||
|
||||
# Predict every changed header/reference before the first mutating invocation.
|
||||
# This fixture has explicit UUIDs for all seven Project::identities families.
|
||||
$sourcePath = Join-Path $repoRoot 'harness/experiments/textual-authoring-20260911/observed/stabilized.arcadia'
|
||||
$source = Read-Exact $sourcePath
|
||||
$sourceHash = Text-Hash $source
|
||||
$aliases = @(
|
||||
[pscustomobject]@{ Family = 'project'; Kind = 'project'; Old = 'alarm_response'; New = 'alarm_suite'; References = 0 },
|
||||
[pscustomobject]@{ Family = 'element'; Kind = 'oa_entity'; Old = 'maintenance'; New = 'maintenance_staff'; References = 1 },
|
||||
[pscustomobject]@{ Family = 'relation'; Kind = 'interaction'; Old = 'alarm_message'; New = 'alarm_payload'; References = 1 },
|
||||
[pscustomobject]@{ Family = 'diagram'; Kind = 'diagram'; Old = 'response_view'; New = 'response_map'; References = 2 },
|
||||
[pscustomobject]@{ Family = 'placement'; Kind = 'node'; Old = 'detector_card'; New = 'detector_tile'; References = 0 },
|
||||
[pscustomobject]@{ Family = 'artifact'; Kind = 'artifact'; Old = 'response_procedure'; New = 'response_handbook'; References = 0 },
|
||||
[pscustomobject]@{ Family = 'trace'; Kind = 'trace'; Old = 'assessment_refinement'; New = 'assessment_alignment'; References = 0 }
|
||||
)
|
||||
$literal = ($aliases | ForEach-Object { '@' + $_.Old + ';' }) -join ' '
|
||||
$source = $source.Replace('Numerical annotations are descriptive.', 'Numerical annotations are descriptive. Literal aliases: ' + $literal)
|
||||
$source += "`r`n// Retain these comment references: $literal`r`n"
|
||||
$expected = $source
|
||||
$predictions = [Collections.Generic.List[object]]::new()
|
||||
foreach ($case in $aliases) {
|
||||
$header = $case.Kind + ' ' + $case.Old + ' '
|
||||
Assert-That (([regex]::Matches($expected, [regex]::Escape($header))).Count -eq 1) ($case.Family + ' has one literal declaration header')
|
||||
$next = $expected.Replace($header, $case.Kind + ' ' + $case.New + ' ')
|
||||
# The known live references occupy these exact fields; prose/comments are excluded.
|
||||
$field = switch ($case.Family) { 'element' { 'source' }; 'relation' { 'exchange' }; 'diagram' { 'diagram' }; default { $null } }
|
||||
if ($null -ne $field) {
|
||||
$oldReference = $field + ' @' + $case.Old + ';'
|
||||
Assert-That (([regex]::Matches($next, [regex]::Escape($oldReference))).Count -eq $case.References) ($case.Family + ' live-reference count')
|
||||
$next = $next.Replace($oldReference, $field + ' @' + $case.New + ';')
|
||||
}
|
||||
$predictions.Add([pscustomobject]@{ Case = $case; Before = $expected; After = $next })
|
||||
$expected = $next
|
||||
}
|
||||
$aliasEntry = Join-Path $runRoot 'aliases/project.arcadia'
|
||||
Put $aliasEntry $source
|
||||
Put (Join-Path $PSScriptRoot 'alias-input.arcadia') $source
|
||||
Put (Join-Path $PSScriptRoot 'alias-expected-final.arcadia') $expected
|
||||
$baseline = Invoke-ArcadiaProbe @('check', $aliasEntry)
|
||||
Assert-That ($baseline.exit_code -eq 0) 'all-family baseline is admitted before mutation'
|
||||
Assert-That ($baseline.identities_to_stabilize -eq 0) 'all-family baseline has no missing identities'
|
||||
Assert-That (([regex]::Matches($source, ' id "[0-9a-f-]{36}"')).Count -eq 40) 'all-family baseline contains exactly 40 authored UUIDs'
|
||||
foreach ($prediction in $predictions) {
|
||||
$case = $prediction.Case
|
||||
$preview = Invoke-ArcadiaProbe @('rename-alias', $aliasEntry, $case.Old, $case.New)
|
||||
Assert-That ($preview.exit_code -eq 0 -and !$preview.written -and $preview.status -ceq 'preview') ($case.Family + ' preview succeeds without write')
|
||||
Assert-That ((Read-Exact $aliasEntry) -ceq $prediction.Before) ($case.Family + ' preview preserves exact input bytes')
|
||||
Assert-That (@($preview.changes).Count -eq 1) ($case.Family + ' reports exactly one changed file')
|
||||
Assert-That ($preview.changes[0].path -ceq 'project.arcadia') ($case.Family + ' preserves file ownership')
|
||||
Assert-That ($preview.changes[0].before -ceq $prediction.Before) ($case.Family + ' reports exact captured text')
|
||||
Assert-That ($preview.changes[0].after -ceq $prediction.After) ($case.Family + ' changes only independently predicted header/live-reference tokens')
|
||||
Assert-That ($preview.identities_to_stabilize -eq 0 -and $null -eq $preview.migration) ($case.Family + ' invents no identity or migration')
|
||||
$written = Invoke-ArcadiaProbe @('rename-alias', $aliasEntry, $case.Old, $case.New, '--write')
|
||||
Assert-That ($written.exit_code -eq 0 -and $written.written -and $written.status -ceq 'written') ($case.Family + ' write succeeds')
|
||||
Assert-That ($written.changes[0].after -ceq $prediction.After) ($case.Family + ' stable separate write invocation has exact preview text')
|
||||
Assert-That ((Read-Exact $aliasEntry) -ceq $prediction.After) ($case.Family + ' persisted text equals independent expectation')
|
||||
}
|
||||
$afterCheck = Invoke-ArcadiaProbe @('check', $aliasEntry)
|
||||
Assert-That ($afterCheck.exit_code -eq 0) 'all-family final source is freshly admitted'
|
||||
$mtime = [IO.File]::GetLastWriteTimeUtc($aliasEntry)
|
||||
$noop = Invoke-ArcadiaProbe @('stabilize', $aliasEntry, '--write')
|
||||
Assert-That ($noop.exit_code -eq 0 -and $noop.status -ceq 'unchanged' -and !$noop.written -and @($noop.changes).Count -eq 0) 'stable all-family source is a true no-op'
|
||||
Assert-That ([IO.File]::GetLastWriteTimeUtc($aliasEntry) -eq $mtime) 'no-op preserves source modification time'
|
||||
Assert-That ((Read-Exact $aliasEntry) -ceq $expected) 'no-op preserves every source byte'
|
||||
Assert-That (@(Get-ChildItem -LiteralPath (Split-Path -Parent $aliasEntry) -File -Recurse -Force).Count -eq 1) 'writes leave no unexpected physical file'
|
||||
|
||||
# Two warnings in one CRLF/Unicode include expose the shifted-second-span trap.
|
||||
$rawEntry = Join-Path $runRoot 'raw/project.arcadia'
|
||||
$workPath = Join-Path $runRoot 'raw/work.arcadia'
|
||||
$entryText = 'arcadia 7; project diagnostic_case "Diagnostics" id "11000000-0000-4000-8000-000000000001" { include "work.arcadia"; }' + "`r`n"
|
||||
$rawWork = "// Пролог 😀`r`n" + 'sa_function first "航海" {}' + "`r`n" + 'sa_function second "Deuxième" {}' + "`r`n"
|
||||
Put $rawEntry $entryText
|
||||
Put $workPath $rawWork
|
||||
function Check-Warnings($report, [string]$rawText, [bool]$explicit) {
|
||||
Assert-That ($report.exit_code -eq 0) 'partial unallocated functions remain valid'
|
||||
$warnings = @($report.diagnostics | Where-Object { $_.code -ceq 'unallocated_leaf' })
|
||||
Assert-That ($warnings.Count -eq 2) 'exactly two raw unallocated-leaf diagnostics without stabilized duplicates'
|
||||
$index = 0
|
||||
foreach ($alias in @('first', 'second')) {
|
||||
$start = $utf8.GetByteCount($rawText.Substring(0, $rawText.IndexOf('sa_function ' + $alias, [StringComparison]::Ordinal)))
|
||||
$warning = @($warnings | Where-Object { $_.location.start -eq $start })
|
||||
Assert-That ($warning.Count -eq 1) ($alias + ' warning starts at original UTF-8 declaration offset')
|
||||
Assert-That ($warning[0].coordinate_space -ceq 'input' -and $warning[0].location.path -ceq 'work.arcadia') ($alias + ' warning identifies raw include')
|
||||
Assert-That ($warning[0].location.line -eq ($index + 2) -and $warning[0].location.column -eq 1) ($alias + ' CRLF/Unicode line and scalar column')
|
||||
$bytes = $utf8.GetBytes($rawText)
|
||||
$location = $warning[0].location
|
||||
Assert-That ($location.end -gt $start -and $location.end -le $bytes.Length) ($alias + ' warning ends inside raw input')
|
||||
$slice = $utf8.GetString($bytes, $start, $location.end - $start)
|
||||
Assert-That ($slice.Contains('sa_function ' + $alias)) ($alias + ' raw diagnostic slice identifies intended declaration')
|
||||
$expectedId = if ($explicit) { '11000000-0000-4000-8000-00000000000' + ($index + 2) } else { $null }
|
||||
Assert-That ($warning[0].object_id -ceq $expectedId) ($alias + ' diagnostic identity has correct authored/provisional status')
|
||||
$index++
|
||||
}
|
||||
}
|
||||
$rawCheck = Invoke-ArcadiaProbe @('check', $rawEntry)
|
||||
Check-Warnings $rawCheck $rawWork $false
|
||||
$rawPreview = Invoke-ArcadiaProbe @('stabilize', $rawEntry)
|
||||
Check-Warnings $rawPreview $rawWork $false
|
||||
Assert-That ((Read-Exact $rawEntry) -ceq $entryText -and (Read-Exact $workPath) -ceq $rawWork) 'raw diagnostics and preview retain both exact source files'
|
||||
Assert-That (@($rawPreview.changes).Count -eq 1 -and $rawPreview.changes[0].path -ceq 'work.arcadia') 'only missing-ID include is proposed for stabilization'
|
||||
$candidateText = $rawPreview.changes[0].after
|
||||
Assert-That ($utf8.GetByteCount($candidateText.Substring(0, $candidateText.IndexOf('sa_function second'))) -gt $utf8.GetByteCount($rawWork.Substring(0, $rawWork.IndexOf('sa_function second')))) 'fixture genuinely shifts second declaration in proposed candidate'
|
||||
$authoredWork = $rawWork.Replace('first "航海" {', 'first "航海" id "11000000-0000-4000-8000-000000000002" {').Replace('second "Deuxième" {', 'second "Deuxième" id "11000000-0000-4000-8000-000000000003" {')
|
||||
Put $workPath $authoredWork
|
||||
$authoredCheck = Invoke-ArcadiaProbe @('lint', $rawEntry)
|
||||
Check-Warnings $authoredCheck $authoredWork $true
|
||||
Assert-That ((Read-Exact $workPath) -ceq $authoredWork) 'authored-ID lint is read-only'
|
||||
|
||||
# A non-BMP symbol before the bad reference distinguishes scalar columns from UTF-16 units.
|
||||
$invalidWork = "// Пролог 😀`r`n" + 'sa_function invalid "Deuxième 🚨" { parent @absent; }' + "`r`n"
|
||||
Put $workPath $invalidWork
|
||||
$invalid = Invoke-ArcadiaProbe @('stabilize', $rawEntry, '--write')
|
||||
Assert-That ($invalid.exit_code -eq 1 -and !$invalid.written -and @($invalid.changes).Count -eq 0) 'invalid raw Unicode source is refused before any candidate write'
|
||||
$referenceIndex = $invalidWork.IndexOf('@absent', [StringComparison]::Ordinal)
|
||||
$referenceStart = $utf8.GetByteCount($invalidWork.Substring(0, $referenceIndex))
|
||||
$errorAt = @($invalid.diagnostics | Where-Object { $_.severity -ceq 'error' -and $_.location.start -eq $referenceStart })
|
||||
Assert-That ($errorAt.Count -eq 1) 'unresolved reference uses exact raw UTF-8 byte start'
|
||||
$linePrefix = $invalidWork.Substring($invalidWork.IndexOf("`n") + 1, $referenceIndex - $invalidWork.IndexOf("`n") - 1)
|
||||
$scalarColumn = @($linePrefix.ToCharArray() | Where-Object { ![char]::IsLowSurrogate($_) }).Count + 1
|
||||
Assert-That ($errorAt[0].location.line -eq 2 -and $errorAt[0].location.column -eq $scalarColumn) 'non-BMP prefix uses Unicode scalar column rather than UTF-16 units'
|
||||
Assert-That ($errorAt[0].coordinate_space -ceq 'input' -and $null -eq $errorAt[0].object_id) 'invalid input exposes neither candidate coordinate nor ephemeral identity'
|
||||
Assert-That ((Read-Exact $workPath) -ceq $invalidWork -and (Read-Exact $rawEntry) -ceq $entryText) 'invalid write retains complete input bundle bytes'
|
||||
Assert-That ((Get-FileHash -LiteralPath $exe).Hash -ceq $expectedExeHash) 'CLI pin remains unchanged after probes'
|
||||
$result = [ordered]@{
|
||||
Reviewer = 'Codex-9e8r6t /root/behavior_audit'
|
||||
RecordedUtc = [DateTime]::UtcNow.ToString('O')
|
||||
BinarySHA256 = $expectedExeHash
|
||||
InitialPublishedFixtureSHA256 = $sourceHash
|
||||
AliasInputSHA256 = Text-Hash $source
|
||||
AliasExpectedFinalSHA256 = Text-Hash $expected
|
||||
CheckCount = $checks.Count
|
||||
Checks = $checks.ToArray()
|
||||
Operations = $operations.ToArray()
|
||||
Limits = 'Independent CLI process/source-byte checks only; no production compile, full Project comparison, concurrency proof, Linux or native GUI claim.'
|
||||
}
|
||||
Put (Join-Path $PSScriptRoot 'result.json') ($result | ConvertTo-Json -Depth 100)
|
||||
Write-Output "PASS: $($checks.Count) independent assertions across $($operations.Count) frozen CLI invocations."
|
||||
@@ -1,465 +0,0 @@
|
||||
{
|
||||
"Reviewer": "Codex-9e8r6t /root/behavior_audit",
|
||||
"RecordedUtc": "2026-09-11T11:59:00.5676616Z",
|
||||
"BinarySHA256": "BAD169F8BE78DCFA74B32714569C886DD2132727080684FC2BA618D5822E3559",
|
||||
"InitialPublishedFixtureSHA256": "E68D7162F6F7656AAD51853E5A38DECD2D84F4CC3B6A9120AF86AC7A64AA92A2",
|
||||
"AliasInputSHA256": "53ACB827AB030203411D0E29907142AA83ECD65C133D5C975DDB9AB74920308B",
|
||||
"AliasExpectedFinalSHA256": "1DCFFDEDEE6B2647F20442D1A8E74BF72AFBF30F1B067F3C947D52117B032C44",
|
||||
"CheckCount": 183,
|
||||
"Checks": [
|
||||
"immutable CLI binary pin",
|
||||
"project has one literal declaration header",
|
||||
"element has one literal declaration header",
|
||||
"element live-reference count",
|
||||
"relation has one literal declaration header",
|
||||
"relation live-reference count",
|
||||
"diagram has one literal declaration header",
|
||||
"diagram live-reference count",
|
||||
"placement has one literal declaration header",
|
||||
"artifact has one literal declaration header",
|
||||
"trace has one literal declaration header",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"all-family baseline is admitted before mutation",
|
||||
"all-family baseline has no missing identities",
|
||||
"all-family baseline contains exactly 40 authored UUIDs",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"project preview succeeds without write",
|
||||
"project preview preserves exact input bytes",
|
||||
"project reports exactly one changed file",
|
||||
"project preserves file ownership",
|
||||
"project reports exact captured text",
|
||||
"project changes only independently predicted header/live-reference tokens",
|
||||
"project invents no identity or migration",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"project write succeeds",
|
||||
"project stable separate write invocation has exact preview text",
|
||||
"project persisted text equals independent expectation",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"element preview succeeds without write",
|
||||
"element preview preserves exact input bytes",
|
||||
"element reports exactly one changed file",
|
||||
"element preserves file ownership",
|
||||
"element reports exact captured text",
|
||||
"element changes only independently predicted header/live-reference tokens",
|
||||
"element invents no identity or migration",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"element write succeeds",
|
||||
"element stable separate write invocation has exact preview text",
|
||||
"element persisted text equals independent expectation",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"relation preview succeeds without write",
|
||||
"relation preview preserves exact input bytes",
|
||||
"relation reports exactly one changed file",
|
||||
"relation preserves file ownership",
|
||||
"relation reports exact captured text",
|
||||
"relation changes only independently predicted header/live-reference tokens",
|
||||
"relation invents no identity or migration",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"relation write succeeds",
|
||||
"relation stable separate write invocation has exact preview text",
|
||||
"relation persisted text equals independent expectation",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"diagram preview succeeds without write",
|
||||
"diagram preview preserves exact input bytes",
|
||||
"diagram reports exactly one changed file",
|
||||
"diagram preserves file ownership",
|
||||
"diagram reports exact captured text",
|
||||
"diagram changes only independently predicted header/live-reference tokens",
|
||||
"diagram invents no identity or migration",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"diagram write succeeds",
|
||||
"diagram stable separate write invocation has exact preview text",
|
||||
"diagram persisted text equals independent expectation",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"placement preview succeeds without write",
|
||||
"placement preview preserves exact input bytes",
|
||||
"placement reports exactly one changed file",
|
||||
"placement preserves file ownership",
|
||||
"placement reports exact captured text",
|
||||
"placement changes only independently predicted header/live-reference tokens",
|
||||
"placement invents no identity or migration",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"placement write succeeds",
|
||||
"placement stable separate write invocation has exact preview text",
|
||||
"placement persisted text equals independent expectation",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"artifact preview succeeds without write",
|
||||
"artifact preview preserves exact input bytes",
|
||||
"artifact reports exactly one changed file",
|
||||
"artifact preserves file ownership",
|
||||
"artifact reports exact captured text",
|
||||
"artifact changes only independently predicted header/live-reference tokens",
|
||||
"artifact invents no identity or migration",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"artifact write succeeds",
|
||||
"artifact stable separate write invocation has exact preview text",
|
||||
"artifact persisted text equals independent expectation",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"trace preview succeeds without write",
|
||||
"trace preview preserves exact input bytes",
|
||||
"trace reports exactly one changed file",
|
||||
"trace preserves file ownership",
|
||||
"trace reports exact captured text",
|
||||
"trace changes only independently predicted header/live-reference tokens",
|
||||
"trace invents no identity or migration",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"trace write succeeds",
|
||||
"trace stable separate write invocation has exact preview text",
|
||||
"trace persisted text equals independent expectation",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"all-family final source is freshly admitted",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"stable all-family source is a true no-op",
|
||||
"no-op preserves source modification time",
|
||||
"no-op preserves every source byte",
|
||||
"writes leave no unexpected physical file",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"partial unallocated functions remain valid",
|
||||
"exactly two raw unallocated-leaf diagnostics without stabilized duplicates",
|
||||
"first warning starts at original UTF-8 declaration offset",
|
||||
"first warning identifies raw include",
|
||||
"first CRLF/Unicode line and scalar column",
|
||||
"first warning ends inside raw input",
|
||||
"first raw diagnostic slice identifies intended declaration",
|
||||
"first diagnostic identity has correct authored/provisional status",
|
||||
"second warning starts at original UTF-8 declaration offset",
|
||||
"second warning identifies raw include",
|
||||
"second CRLF/Unicode line and scalar column",
|
||||
"second warning ends inside raw input",
|
||||
"second raw diagnostic slice identifies intended declaration",
|
||||
"second diagnostic identity has correct authored/provisional status",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"partial unallocated functions remain valid",
|
||||
"exactly two raw unallocated-leaf diagnostics without stabilized duplicates",
|
||||
"first warning starts at original UTF-8 declaration offset",
|
||||
"first warning identifies raw include",
|
||||
"first CRLF/Unicode line and scalar column",
|
||||
"first warning ends inside raw input",
|
||||
"first raw diagnostic slice identifies intended declaration",
|
||||
"first diagnostic identity has correct authored/provisional status",
|
||||
"second warning starts at original UTF-8 declaration offset",
|
||||
"second warning identifies raw include",
|
||||
"second CRLF/Unicode line and scalar column",
|
||||
"second warning ends inside raw input",
|
||||
"second raw diagnostic slice identifies intended declaration",
|
||||
"second diagnostic identity has correct authored/provisional status",
|
||||
"raw diagnostics and preview retain both exact source files",
|
||||
"only missing-ID include is proposed for stabilization",
|
||||
"fixture genuinely shifts second declaration in proposed candidate",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"partial unallocated functions remain valid",
|
||||
"exactly two raw unallocated-leaf diagnostics without stabilized duplicates",
|
||||
"first warning starts at original UTF-8 declaration offset",
|
||||
"first warning identifies raw include",
|
||||
"first CRLF/Unicode line and scalar column",
|
||||
"first warning ends inside raw input",
|
||||
"first raw diagnostic slice identifies intended declaration",
|
||||
"first diagnostic identity has correct authored/provisional status",
|
||||
"second warning starts at original UTF-8 declaration offset",
|
||||
"second warning identifies raw include",
|
||||
"second CRLF/Unicode line and scalar column",
|
||||
"second warning ends inside raw input",
|
||||
"second raw diagnostic slice identifies intended declaration",
|
||||
"second diagnostic identity has correct authored/provisional status",
|
||||
"authored-ID lint is read-only",
|
||||
"process exit agrees with JSON",
|
||||
"JSON invocation emits no mixed stderr",
|
||||
"invalid raw Unicode source is refused before any candidate write",
|
||||
"unresolved reference uses exact raw UTF-8 byte start",
|
||||
"non-BMP prefix uses Unicode scalar column rather than UTF-16 units",
|
||||
"invalid input exposes neither candidate coordinate nor ephemeral identity",
|
||||
"invalid write retains complete input bundle bytes",
|
||||
"CLI pin remains unchanged after probes"
|
||||
],
|
||||
"Operations": [
|
||||
{
|
||||
"Arguments": [
|
||||
"check",
|
||||
"<fixture>\\aliases\\project.arcadia"
|
||||
],
|
||||
"Exit": 0,
|
||||
"Status": "checked",
|
||||
"Changes": 0,
|
||||
"Diagnostics": 0,
|
||||
"StdoutSHA256": "251E0C7CAF9243AFBCDCCD371707B8BFFF63CCF47537DABE9C398B4F3BBDC165"
|
||||
},
|
||||
{
|
||||
"Arguments": [
|
||||
"rename-alias",
|
||||
"<fixture>\\aliases\\project.arcadia",
|
||||
"alarm_response",
|
||||
"alarm_suite"
|
||||
],
|
||||
"Exit": 0,
|
||||
"Status": "preview",
|
||||
"Changes": 1,
|
||||
"Diagnostics": 0,
|
||||
"StdoutSHA256": "E577D97B27E93CF4395EA67C01177FB7427EC28AE7D83629BD3CAB82849AE712"
|
||||
},
|
||||
{
|
||||
"Arguments": [
|
||||
"rename-alias",
|
||||
"<fixture>\\aliases\\project.arcadia",
|
||||
"alarm_response",
|
||||
"alarm_suite",
|
||||
"--write"
|
||||
],
|
||||
"Exit": 0,
|
||||
"Status": "written",
|
||||
"Changes": 1,
|
||||
"Diagnostics": 0,
|
||||
"StdoutSHA256": "1134EA13A5674D0AF0237BFB08C720FF046C0D82C8AE916AA6F97C40A009471F"
|
||||
},
|
||||
{
|
||||
"Arguments": [
|
||||
"rename-alias",
|
||||
"<fixture>\\aliases\\project.arcadia",
|
||||
"maintenance",
|
||||
"maintenance_staff"
|
||||
],
|
||||
"Exit": 0,
|
||||
"Status": "preview",
|
||||
"Changes": 1,
|
||||
"Diagnostics": 0,
|
||||
"StdoutSHA256": "05CFEFA2CDD8612628906E81BA994446FC2D408642CAD8C8EAE1B0B0BDBFE5D5"
|
||||
},
|
||||
{
|
||||
"Arguments": [
|
||||
"rename-alias",
|
||||
"<fixture>\\aliases\\project.arcadia",
|
||||
"maintenance",
|
||||
"maintenance_staff",
|
||||
"--write"
|
||||
],
|
||||
"Exit": 0,
|
||||
"Status": "written",
|
||||
"Changes": 1,
|
||||
"Diagnostics": 0,
|
||||
"StdoutSHA256": "54B496AFCC671910E5109FFF3B65CFE0DC1BEA8D85871BDBC976177F28962AEF"
|
||||
},
|
||||
{
|
||||
"Arguments": [
|
||||
"rename-alias",
|
||||
"<fixture>\\aliases\\project.arcadia",
|
||||
"alarm_message",
|
||||
"alarm_payload"
|
||||
],
|
||||
"Exit": 0,
|
||||
"Status": "preview",
|
||||
"Changes": 1,
|
||||
"Diagnostics": 0,
|
||||
"StdoutSHA256": "61309AADFF80386FA73FA9E7005C4C45548C56BC015BAF11750DCB00CD379E92"
|
||||
},
|
||||
{
|
||||
"Arguments": [
|
||||
"rename-alias",
|
||||
"<fixture>\\aliases\\project.arcadia",
|
||||
"alarm_message",
|
||||
"alarm_payload",
|
||||
"--write"
|
||||
],
|
||||
"Exit": 0,
|
||||
"Status": "written",
|
||||
"Changes": 1,
|
||||
"Diagnostics": 0,
|
||||
"StdoutSHA256": "EB0317BF9C7A6C54644748D7F8BFD04E082469AE98EAAA1F39DCC2AED9EDFDE7"
|
||||
},
|
||||
{
|
||||
"Arguments": [
|
||||
"rename-alias",
|
||||
"<fixture>\\aliases\\project.arcadia",
|
||||
"response_view",
|
||||
"response_map"
|
||||
],
|
||||
"Exit": 0,
|
||||
"Status": "preview",
|
||||
"Changes": 1,
|
||||
"Diagnostics": 0,
|
||||
"StdoutSHA256": "3D2325D62D6F7D7FB7D7B7E162800DBBB15A4328BC1C5C0BF98EFD794E4AA472"
|
||||
},
|
||||
{
|
||||
"Arguments": [
|
||||
"rename-alias",
|
||||
"<fixture>\\aliases\\project.arcadia",
|
||||
"response_view",
|
||||
"response_map",
|
||||
"--write"
|
||||
],
|
||||
"Exit": 0,
|
||||
"Status": "written",
|
||||
"Changes": 1,
|
||||
"Diagnostics": 0,
|
||||
"StdoutSHA256": "E77B284EAFFBB090B73035FD584CE1D184C601112BCC0D5266D42F1CD7D2D185"
|
||||
},
|
||||
{
|
||||
"Arguments": [
|
||||
"rename-alias",
|
||||
"<fixture>\\aliases\\project.arcadia",
|
||||
"detector_card",
|
||||
"detector_tile"
|
||||
],
|
||||
"Exit": 0,
|
||||
"Status": "preview",
|
||||
"Changes": 1,
|
||||
"Diagnostics": 0,
|
||||
"StdoutSHA256": "8DBA5013A3404CB5719573CDE66C92768C764B363C937646B79D848EA356D275"
|
||||
},
|
||||
{
|
||||
"Arguments": [
|
||||
"rename-alias",
|
||||
"<fixture>\\aliases\\project.arcadia",
|
||||
"detector_card",
|
||||
"detector_tile",
|
||||
"--write"
|
||||
],
|
||||
"Exit": 0,
|
||||
"Status": "written",
|
||||
"Changes": 1,
|
||||
"Diagnostics": 0,
|
||||
"StdoutSHA256": "EB444F39F873C58A8192C0A5AFC2C48FCAF1CAB333231C66471F71D1C1D2486A"
|
||||
},
|
||||
{
|
||||
"Arguments": [
|
||||
"rename-alias",
|
||||
"<fixture>\\aliases\\project.arcadia",
|
||||
"response_procedure",
|
||||
"response_handbook"
|
||||
],
|
||||
"Exit": 0,
|
||||
"Status": "preview",
|
||||
"Changes": 1,
|
||||
"Diagnostics": 0,
|
||||
"StdoutSHA256": "6613EDCABEF4D8AC81E9FC21000FDE7AF10726E882BAF106ECA7991E553C4F53"
|
||||
},
|
||||
{
|
||||
"Arguments": [
|
||||
"rename-alias",
|
||||
"<fixture>\\aliases\\project.arcadia",
|
||||
"response_procedure",
|
||||
"response_handbook",
|
||||
"--write"
|
||||
],
|
||||
"Exit": 0,
|
||||
"Status": "written",
|
||||
"Changes": 1,
|
||||
"Diagnostics": 0,
|
||||
"StdoutSHA256": "0B740F86D607AD194EBD93D63DA65210A39DD35B1B3757CE679EC1A84FB8D3B9"
|
||||
},
|
||||
{
|
||||
"Arguments": [
|
||||
"rename-alias",
|
||||
"<fixture>\\aliases\\project.arcadia",
|
||||
"assessment_refinement",
|
||||
"assessment_alignment"
|
||||
],
|
||||
"Exit": 0,
|
||||
"Status": "preview",
|
||||
"Changes": 1,
|
||||
"Diagnostics": 0,
|
||||
"StdoutSHA256": "101A1D9E89F482E6607B8A4855A673F56897818969333F6ED0D5F5749BFA739D"
|
||||
},
|
||||
{
|
||||
"Arguments": [
|
||||
"rename-alias",
|
||||
"<fixture>\\aliases\\project.arcadia",
|
||||
"assessment_refinement",
|
||||
"assessment_alignment",
|
||||
"--write"
|
||||
],
|
||||
"Exit": 0,
|
||||
"Status": "written",
|
||||
"Changes": 1,
|
||||
"Diagnostics": 0,
|
||||
"StdoutSHA256": "DA48C339A3C387BE06F6C3DCD799B3F98E75F6F97F760195EFFB9AB1C86F6EE7"
|
||||
},
|
||||
{
|
||||
"Arguments": [
|
||||
"check",
|
||||
"<fixture>\\aliases\\project.arcadia"
|
||||
],
|
||||
"Exit": 0,
|
||||
"Status": "checked",
|
||||
"Changes": 0,
|
||||
"Diagnostics": 0,
|
||||
"StdoutSHA256": "251E0C7CAF9243AFBCDCCD371707B8BFFF63CCF47537DABE9C398B4F3BBDC165"
|
||||
},
|
||||
{
|
||||
"Arguments": [
|
||||
"stabilize",
|
||||
"<fixture>\\aliases\\project.arcadia",
|
||||
"--write"
|
||||
],
|
||||
"Exit": 0,
|
||||
"Status": "unchanged",
|
||||
"Changes": 0,
|
||||
"Diagnostics": 0,
|
||||
"StdoutSHA256": "FD9DF6ECDEF4F60AFDB07C8B37848E65ABE2E995A8A9F21506A322BE173753BE"
|
||||
},
|
||||
{
|
||||
"Arguments": [
|
||||
"check",
|
||||
"<fixture>\\raw\\project.arcadia"
|
||||
],
|
||||
"Exit": 0,
|
||||
"Status": "checked",
|
||||
"Changes": 0,
|
||||
"Diagnostics": 5,
|
||||
"StdoutSHA256": "76363050F8B22A12D5A587156C7C6846ABD9CB087378735068CE0AFC5FAA798D"
|
||||
},
|
||||
{
|
||||
"Arguments": [
|
||||
"stabilize",
|
||||
"<fixture>\\raw\\project.arcadia"
|
||||
],
|
||||
"Exit": 0,
|
||||
"Status": "preview",
|
||||
"Changes": 1,
|
||||
"Diagnostics": 5,
|
||||
"StdoutSHA256": "4B832B7D683AEED0ABCCE29262561CF3C77DF0CB1E965990C28B860ED0002F7A"
|
||||
},
|
||||
{
|
||||
"Arguments": [
|
||||
"lint",
|
||||
"<fixture>\\raw\\project.arcadia"
|
||||
],
|
||||
"Exit": 0,
|
||||
"Status": "checked",
|
||||
"Changes": 0,
|
||||
"Diagnostics": 5,
|
||||
"StdoutSHA256": "00DDDFC5A535317F6D4B5F8FFF46FACEDAF49E89D9D1ED319C4EC3D24B018BEC"
|
||||
},
|
||||
{
|
||||
"Arguments": [
|
||||
"stabilize",
|
||||
"<fixture>\\raw\\project.arcadia",
|
||||
"--write"
|
||||
],
|
||||
"Exit": 1,
|
||||
"Status": "invalid",
|
||||
"Changes": 0,
|
||||
"Diagnostics": 1,
|
||||
"StdoutSHA256": "0813BD7BEF5B4B20D500A3B547C07F52C10C3E074EC6B24B57021F42FC1AE5D1"
|
||||
}
|
||||
],
|
||||
"Limits": "Independent CLI process/source-byte checks only; no production compile, full Project comparison, concurrency proof, Linux or native GUI claim."
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
# Packaged authoring GUI acceptance — 2026-09-11
|
||||
|
||||
Codex-9e8r6t `/root`, Windows. Bounded clean-package verification for
|
||||
`0.2.0-preview.authoring.20260911`; the full OA/SA release remains unfinished.
|
||||
|
||||
## Artifact and environment
|
||||
|
||||
ZIP SHA256 `664F30B6217689ACA1492EB263B3D15777067D732FA890EEB028F435209DADFA`,
|
||||
32,618,262 bytes, 559 files. A fresh extraction independently verified every
|
||||
558 manifest entry and the exact physical inventory including SHA256SUMS.txt.
|
||||
Desktop D4D9C2BD…B20C31 and libunwind F45604A1…0E1AE7D4 match the package and
|
||||
earlier native authoring artifacts. The source/provenance/notice boundary is in
|
||||
`release-authoring-prerelease-preparation-20260911.md`.
|
||||
|
||||
The first launch harness stopped before spawning any process: Python's copied
|
||||
Windows environment had uppercase SYSTEMROOT, while the harness read SystemRoot.
|
||||
That extracted directory is preserved. The corrected second attempt uses a new
|
||||
directory and reads the existing uppercase key. No application or package file
|
||||
was changed to correct this harness error.
|
||||
|
||||
Trial `.local/authoring-prerelease-gui-20260911-v2/` records the exact launch.
|
||||
PATH contains only the extracted package, C:\Windows\System32 and C:\Windows.
|
||||
LOCALAPPDATA and APPDATA point to fresh private profile directories. The desktop
|
||||
was launched from the extracted package directory as PID10800, native window984068.
|
||||
Loaded module inspection found only the packaged executable/libunwind outside
|
||||
C:\Windows; no compiler, Cargo target or development runtime path was loaded.
|
||||
This is a clean application runtime/profile on the current Windows machine,
|
||||
not a claim of a fresh operating-system installation.
|
||||
|
||||
## Native observations
|
||||
|
||||
- Fresh Home showed Untitled mission, no recent models, and usable example/help
|
||||
actions. The initial occluded screenshot was not used for acceptance; explicit
|
||||
activation exposed the packaged window before input.
|
||||
- Open water-pressure walkthrough loaded the packaged multi-file example:
|
||||
94 objects, 62 links and one model check. OA architecture showed allocated
|
||||
activities nested inside Control room and Field technician, with exchanges.
|
||||
- Ordinary Ctrl+S completed. All 558 checksum entries still match and the exact
|
||||
559-file package inventory remains unchanged, including every example source.
|
||||
- Switching to System Analysis rendered the service boundary, allocated functions,
|
||||
SystemActor and ports. Behavior & scenarios rendered the pressure-supervision
|
||||
state machine and its editing toolbar.
|
||||
- Engineering guide opened its bundled content and scenario-authoring topic.
|
||||
- Alt+F4 closed the clean-package instance; a fresh window/process inventory
|
||||
confirmed it had exited, while the separate native scenario trial remained open.
|
||||
|
||||
Screenshots, launch metadata, module inventory, post-Save exact comparison and
|
||||
stdout/stderr are retained in the trial directory. No runtime error was present
|
||||
in stderr during these checks. The current host's wider/minimum named creation,
|
||||
source parity, Undo/Redo and recovery checks are separately tied to the same
|
||||
D4D9 executable; they were not all repeated as package smoke tests.
|
||||
|
||||
## Limits
|
||||
|
||||
This milestone uses development-profile Windows x64 binaries. It includes the
|
||||
desktop, server, examples, offline documentation and dependency notices. It does
|
||||
not include the newer contextual-source or reference-chooser work, B4 lifetime
|
||||
changes, the separately committed CLI executable, completed executable analysis/
|
||||
quantity/library semantics, or a verified Linux binary. Some long labels and
|
||||
state-transition text remain crowded, and scenario forms can retain a prior
|
||||
scroll offset. Those limitations remain visible in the release notes.
|
||||
|
||||
Independent package/server/restart verification is recorded separately. This
|
||||
bounded acceptance supports publishing a working prerelease, not full release
|
||||
completion or optimized-build acceptance.
|
||||
@@ -1,124 +0,0 @@
|
||||
# Authoring milestone prerelease preparation — 2026-09-11
|
||||
|
||||
Owner: Codex-9e8r6t /root/palette_finish. The initial read-only inventory and plan are followed below by root-authorized isolated preparation and successful packaging results. A new archive now exists; clean-extraction acceptance and publication remain separate gates.
|
||||
|
||||
## Result and boundary
|
||||
|
||||
A coherent Windows milestone package can pair the immutable D4D9 desktop with the preserved EFEE server using the existing strict packaging script. Both have recorded build/validation evidence. Independent byte comparisons establish the shared source relationship; executable dates and the common `0.2.0` version are not used as evidence of equivalence.
|
||||
|
||||
The package must be generated from an isolated committed snapshot corresponding to those binaries. Running the script against the evolving working tree would describe different local source. No checkout, Cargo metadata, package invocation, application launch, or native input was performed for this inventory. The separately authorized oracle compiler slot and its results are recorded in `release-staged-source-authoring-oracle-20260911.md`.
|
||||
|
||||
The current script cannot package `arcadia.exe` as a CLI root. It accepts desktop/server roots only. Adding the CLI to an older package without a corresponding notice/source inventory is not proposed.
|
||||
|
||||
## Exact frozen inputs
|
||||
|
||||
Paths below are relative to the repository root. SHA256 values were reread from the actual files.
|
||||
|
||||
| Artifact | Bytes | SHA256 |
|
||||
| --- | ---: | --- |
|
||||
| `.local/behavior-creation-20260911/gui-verified/arcadia-studio.exe` | 111163904 | `d4d9c2bd937e301ff1009d7cb93a34ee5aa6dfc837ff7e87d468c85371b20c31` |
|
||||
| `.local/native-qa/bin-source7-post-routing/arcadia-server.exe` | 39743488 | `efeeb89c0d2e0f93df4472cb3e9f48b2b722a37ae1043c90a5a56f2e845fe665` |
|
||||
| `.local/behavior-creation-20260911/gui-verified/libunwind.dll` | 190976 | `f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4` |
|
||||
| `.local/source-cli-20260911/verified/arcadia.exe` — separate candidate | 20312064 | `bad169f8be78dcfa74b32714569c886dd2132727080684fc2ba618d5822e3559` |
|
||||
|
||||
The preserved server and mutable `.local/target-gnullvm/debug/arcadia-server.exe` currently have identical hashes. The preserved path is the proposed package input. The GUI's sibling `manifest.json` records its successful build from 08:49:00.3082487Z to 08:49:20.3746807Z on September 11, and 253 input hashes. It explains the behavior-equivalent history-fields let-chain correction after the full 653-test snapshot, followed by strict Clippy, formatting and the focused 63 history tests. The later contextual-source implementation is absent from this frozen GUI.
|
||||
|
||||
The server's hash is recorded in `.local/native-qa/source7-post-routing-binary-hashes.json` and the September 7 native acceptance report. `.local/source7-post-routing-gates-status.json` records the focused server gates, full server gate, formatting, workspace strict Clippy and successful workspace binary build (08:27:18.0824157Z start; 102.218 seconds). The contemporaneous report records server 56/56 and desktop 564/564. These historical gates are attributed to their original run, not rerun by this inventory.
|
||||
|
||||
## Source relationship, including exact line endings
|
||||
|
||||
Git commit `2ec72d4241fa8971fa6fe9f626937de9dfd7c7eb` and earlier `4bb4aaf` each match 246 of the GUI manifest's 253 raw input hashes. The seven differences are exactly LF versus CRLF. Converting each committed LF file to CRLF reproduces the expected GUI hash; current working bytes also match that expected hash:
|
||||
|
||||
```text
|
||||
crates/oa-desktop/src/semantic_review/artifacts_view.rs
|
||||
crates/oa-desktop/src/semantic_review/attachments_view.rs
|
||||
crates/oa-desktop/src/semantic_review/keyed_rows.rs
|
||||
crates/oa-desktop/src/semantic_review/keyed_tests.rs
|
||||
crates/oa-desktop/src/semantic_review/keyed.rs
|
||||
crates/oa-desktop/src/semantic_review/ordered.rs
|
||||
docs/openapi.json
|
||||
```
|
||||
|
||||
This is not a claim that all 253 Git blobs already match. An isolated exact-byte snapshot needs these seven byte restorations and scoped `-text` attributes so a subsequent Git checkout preserves them. The GUI manifest's original head `d114a271c5ecffcd7960fb8aa44d45989c10dc33` is historical context; its input manifest is the more precise comparison boundary. No live-tree line endings were changed.
|
||||
|
||||
The preserved server source snapshot is `.local/linux-qa/runtime/source7-20260907T082753190288Z.tar.gz`, SHA256 `cbb371a93a562e1e641d7629fe351e016d63e66fc9112499b3fed6d5cd171f78`. All 120 recorded core/server files were reread from that tar and match their recorded hashes and commit `2ec72d4` exactly. Its root Cargo manifest, lock, three shared build files and raw OpenAPI file match the GUI manifest as well. In particular:
|
||||
|
||||
| Shared input | SHA256 |
|
||||
| --- | --- |
|
||||
| `Cargo.toml` | `9bd014df6e8124c769d551a660f7a511341ff196f80c70008dbb9250efd22081` |
|
||||
| `Cargo.lock` | `a6e070ed59b0ba19f18d60e8104494b777b470bd0268ae348cbe2b229790449f` |
|
||||
| `docs/openapi.json` raw CRLF bytes | `c6ae512f791a939eaf6ec5e5e78e9468bdee0e028092d00098bd90ad9498d967` |
|
||||
|
||||
The snapshot was also used for a Linux gate; that fact alone does not attest the Windows executable. The conclusion uses the preserved Windows hash/build records, explicit source comparison, shared dependency inputs and compiler evidence together. It is not a reproducible-build claim.
|
||||
|
||||
## Compiler, runtime and notice evidence
|
||||
|
||||
Direct `rustc -vV` from the configured toolchain reports Rust 1.98.1, commit `48a229ceaefd4985c50990b14116b6d856af0985`, host `x86_64-pc-windows-gnullvm`, LLVM 22.1.8. All three executables contain this exact `/rustc/<commit>/` path. All 58 current target-library hashes match the prior strict Windows notice inventory. Embedded compiler paths corroborate provenance; they do not establish every linker input.
|
||||
|
||||
The packager's required runtime is `.local/toolchain/llvm-mingw-20260826-ucrt-x86_64/x86_64-w64-mingw32/bin/libunwind.dll`, which matches the frozen sibling DLL above. The LLVM-MinGW root `LICENSE.TXT` is 15141 bytes, SHA256 `8d85c1057d742e597985c7d4e6320b015a9139385cff4cbae06ffc0ebe89afee`.
|
||||
|
||||
The existing notice-complete packaging verification archive is `.local/packaging-20260910/windows-b/ArcadiaStudio-packaging-check-20260910-b-x86_64-pc-windows-gnullvm.zip`, 32309306 bytes, 557 files, SHA256 `bc27e398d7268a4ee6e6c52ccc89f32bb0a4a970020ca198963501c49146e8c8`. It validates the packaging mechanism and its earlier inputs. It does not contain the proposed D4D9 combination and cannot be relabeled as that milestone. Prior Linux notice inventories and the inert ELF packaging fixture likewise do not establish a runnable Linux prerelease.
|
||||
|
||||
## Concrete preparation and packaging plan
|
||||
|
||||
Root selects the final version and coordinates checkout creation and the metadata lease. An illustrative version is `0.2.0-authoring.20260911.1`; it is not reserved by this report.
|
||||
|
||||
1. Create an isolated checkout under `.local/authoring-prerelease-20260911/source` from `2ec72d4`. Restore only the seven identified CRLF inputs, add exact-path `-text` attributes there, and commit the isolated byte-preserving snapshot. Recheck all 253 GUI inputs, all 120 server files, shared manifests/build inputs and a Git checkout round trip before packaging. Record the resulting commit, original base and seven restorations honestly. Do not describe the derived snapshot as the historical build commit.
|
||||
2. Copy the frozen desktop and preserved server into a fresh private `bin` directory; reread both exact hashes. Keep the original GUI build manifest, server hash/gate records and source comparison inventory as provenance attachments. Do not use mutable target output as the source of the copy.
|
||||
3. Prepare the packager's build-evidence JSON with `source_identity`, `profile`, `compiler`, `target` and `binary_sha256` entries for both executables. These strings must distinguish the reconstructed exact input snapshot from original build records. `Read-PackageBuildEvidence` checks target and binary hashes; descriptive source/compiler/profile fields remain caller assertions, so this report's underlying evidence remains necessary.
|
||||
4. After release of the writer lease, invoke the script from the isolated checkout with the configured toolchain environment and absolute paths. This performs locked offline Cargo metadata, not compilation. Keep the strict font/supplement and license checks intact. It must produce a fresh notice inventory from that snapshot, not reuse a prior inventory against different local files.
|
||||
5. Record the final ZIP checksum, exact file inventory and script logs. The script verifies archive membership and hashes, uses deterministic archive timestamps, checks ordinary PE imports and runtime closure, and refuses existing output/claim paths. Its successful result is still `NativeAcceptancePending`.
|
||||
6. Root performs clean extraction with a clean runtime PATH, desktop/server launch and the chosen bounded example/first-run checks, then publishes the versioned runnable artifact with truthful milestone limitations. Dynamically loaded and delayed imports remain runtime checks. The prior native authoring report does not replace clean-package launch acceptance.
|
||||
|
||||
Illustrative invocation after those prerequisites, with variables deliberately supplied by the coordinating executor:
|
||||
|
||||
```powershell
|
||||
& (Join-Path $milestoneSource 'scripts/package-windows.ps1') `
|
||||
-Target x86_64-pc-windows-gnullvm `
|
||||
-Version $milestoneVersion `
|
||||
-BinaryDirectory $milestoneBin `
|
||||
-LlvmMinGWRoot $verifiedLlvmMinGWRoot `
|
||||
-BuildEvidence $milestoneBuildEvidence `
|
||||
-Python 'C:\Users\User\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\python.exe' `
|
||||
-OutputRoot $milestoneOutputRoot
|
||||
```
|
||||
|
||||
With the illustrative version and private output root, the expected new artifact path is `.local/authoring-prerelease-20260911/packages/ArcadiaStudio-0.2.0-authoring.20260911.1-x86_64-pc-windows-gnullvm.zip`. This path is a plan, not an existing notice-complete deliverable.
|
||||
|
||||
The current `Get-ArcadiaNotices` root allowlist accepts `oa-desktop` and/or `oa-server`, and `package-windows.ps1` requires both named executables. A separate CLI package needs explicitly coordinated support for its root, exact CLI source/workspace snapshot, documentation and runtime/notice closure. Its new local workspace/lock entry is absent from the older GUI snapshot. CLI author tests and independent 183-assertion review establish its scoped functionality, not an archive's redistribution completeness.
|
||||
|
||||
## Coordination and durable evidence
|
||||
|
||||
Gitea issue 1 comment 172 coordinates publication ownership. Later user comment 178 explicitly authorizes programmatic reuse of existing MCP credentials for authenticated Git and direct Gitea REST asset upload, without printing or committing secrets. Thus the previously reported missing MCP upload method is not itself a publication blocker. Publication remains with root/the coordinated host; this worker neither read credentials nor attempted publication.
|
||||
|
||||
Private reproducible inventory: `.local/authoring-prerelease-20260911/inventory.py`, `inventory-output.log` and `inventory.json`. The JSON SHA256 is `3c78dea4e93dae186fd24ab826c6680178a2c7849c270a22deae2ff732f1857b`; it retains individual comparisons and all measured artifact hashes. An initial inventory script key typo (`target_libraries` instead of the existing `target_libraries_sha256`) was corrected before the successful inventory. It was not an application, compiler or packaging failure.
|
||||
|
||||
Next action belongs to root: choose this frozen milestone or wait for the newer contextual-source GUI, authorize the corresponding isolated source preparation and metadata window, and reserve the version. A newer GUI requires its own exact source/build comparison and native acceptance; none of this report silently transfers D4D9's evidence to it.
|
||||
|
||||
## Authorized preparation follow-up
|
||||
|
||||
Root selected `0.2.0-preview.authoring.20260911`, the frozen GUI/server milestone based on `2ec72d4`, and authorized private source/binary/evidence preparation and the unchanged package invocation. CLI source was separately committed and published at `01b76a1e89e93b7992f39e63560aa8b2ca2f69e0`. The newer AGENTS milestone-publication instruction exists on that current branch. Its changed Cargo workspace and lock are intentionally absent from the exact older GUI/server input snapshot.
|
||||
|
||||
The initially authorized checkout at `01b76a1` was cleanly retargeted to detached `2ec72d4` after root confirmed this source boundary. Private auxiliary commit `234e01e27d6935d213097b345d91d369f808af82` preserves the seven exact CRLF restorations and their scoped `-text` attributes. Ignoring end-of-line differences, its only diff is nine `.gitattributes` lines. It is a derived input-preservation commit, not the original GUI/server build commit or the chosen public base tag. The main working tree and shared packaging scripts were not changed.
|
||||
|
||||
`.local/authoring-prerelease-20260911/prepare.py` verified all 253 GUI inputs, all 120 server files and the three copied executable/runtime hashes. `preparation.json` records the comparisons. `verify-committed.py` additionally reads the Git blobs and a fresh `git checkout-index` output. `package.ps1` supplies exact frozen binary hashes and the explicit provenance assertion in `build-evidence.json`, invoking the isolated unchanged script with the selected version. The metadata slot was explicitly granted by release_audit and returned after the single Cargo metadata call; remaining notice scanning and archive work are read-only with respect to Cargo. Terminal packaging and round-trip results follow below when measured.
|
||||
|
||||
## Bounded separate CLI packaging design proposal
|
||||
|
||||
This proposal changes no scripts. Extend the notice collector's root allowlist to include `oa-cli`, keeping its existing exact local-source, lock, normal/build graph, Rust/runtime and supplement checks unchanged. Add a CLI-specific prebuilt entry point requiring exactly `arcadia.exe` and its checked runtime closure, invoking the collector with only `oa-cli` as its declared root. Share the existing ordinary PE import inspection rather than copying a divergent checker, and retain the common build-evidence, reserved-output, checksum and archive-readback helpers.
|
||||
|
||||
Its documentation closure should start with a CLI-specific package README and `docs/source-cli.md`, with examples linked by those documents. It should not promise a desktop executable. Use the exact `01b76a1` CLI workspace/lock/source inputs and preserved BAD169F8 build evidence, independently verify their hashes, and version it distinctly (for example an `ArcadiaCLI-…` archive). CLI native smoke is `--version`, `--help`, valid explicit-root check, preview byte equality and explicit write in a disposable example. Reject missing runtime, wrong binary hash, missing notice, unknown root, existing output and changed-source evidence just as the existing workflow does. Tests must also confirm that adding the allowlisted CLI root does not change the desktop/server default roots. This is a small packaging extension, not a saved-plan CLI feature or a waiver of distribution notices.
|
||||
|
||||
## Terminal package result and frozen handoff
|
||||
|
||||
The first package invocation passed, starting `2026-09-11T12:17:30.7730570Z`, finishing `12:21:23.0302800Z`, duration **232.2812442 seconds**. No packaging failure or retry occurred. The checked-in packaging scripts were used without modification. The separate committed-blob/fresh-checkout test passed all 253 input comparisons and confirmed the isolated source worktree remained clean.
|
||||
|
||||
Frozen ZIP: `.local/authoring-prerelease-20260911/packages/ArcadiaStudio-0.2.0-preview.authoring.20260911-x86_64-pc-windows-gnullvm.zip`.
|
||||
|
||||
- Bytes: **32618262**; package files: **559**.
|
||||
- SHA256: **`664f30b6217689aca1492eb263b3d15777067d732fa890eeb028f435209dadfa`**.
|
||||
- Notice inventory: **266 packages**, declared roots `oa-desktop` and `oa-server`; original lock hash `a6e070ed59b0ba19f18d60e8104494b777b470bd0268ae348cbe2b229790449f` retained.
|
||||
- Executables/runtime: exact D4D9 desktop, EFEE server and F456 libunwind identities from the table above.
|
||||
- Package script full archive membership/hash readback: **PASS**. Returned native status: **Pending**.
|
||||
|
||||
Private logs are `package-initial.log` and `package-initial-result.json`; `committed-roundtrip.json` retains the 253 committed and rechecked input hashes. The sibling package directory, ZIP and checksum are frozen. Root and the independent reviewer received the exact path, size and hash. The reviewer owns clean extraction and private packaged-server checks; root owns clean-package GUI acceptance and publication. This result does not claim those checks, a runnable Linux artifact, release-profile optimization, or native acceptance of the newer contextual-source GUI.
|
||||
@@ -1,33 +0,0 @@
|
||||
# Published Windows authoring milestone — 2026-09-11
|
||||
|
||||
Codex-9e8r6t `/root`. Published at **12:40:51 UTC** as a prerelease:
|
||||
[v0.2.0-preview.authoring.20260911](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/releases/tag/v0.2.0-preview.authoring.20260911).
|
||||
|
||||
Release ID 1 is public/non-draft and marked prerelease. Post-publication tag
|
||||
readback matches `2ec72d4241fa8971fa6fe9f626937de9dfd7c7eb`. The exact measured
|
||||
input reconstruction `234e01e27d6935d213097b345d91d369f808af82` is separately
|
||||
pushed and read back on `codex/authoring-prerelease-source-20260911`.
|
||||
|
||||
Three assets were uploaded through the documented Gitea REST attachment endpoint
|
||||
using the existing MCP credential, under user issue 1 comment 178 authorization.
|
||||
Credentials were read only into process memory and were not printed, persisted
|
||||
in Git configuration, placed in remote URLs or committed. Every uploaded asset
|
||||
was downloaded and compared by full byte length and SHA256 before publication.
|
||||
|
||||
| Asset | Bytes | SHA256 |
|
||||
| --- | ---: | --- |
|
||||
| ArcadiaStudio Windows gnullvm ZIP | 32,618,262 | `664f30b6217689aca1492eb263b3d15777067d732fa890eeb028f435209dadfa` |
|
||||
| ZIP checksum file | 143 | `ea553a85f35d2d48f229dd4cd00ac0c3d4a6e9dafa58273004b2c49d1279f856` |
|
||||
| VALIDATION.md | 29,308 | `c2a70be735c780d3e468d5f12643fb00be75673434211366739926536bbbe9b9` |
|
||||
|
||||
The release notes give extraction/launch instructions, exact source provenance,
|
||||
verified behavior and explicit development-profile/usability/semantics/Linux
|
||||
limitations. The ZIP contains Studio, the server, examples, offline help and
|
||||
notices. The independently prepared CLI ZIP is a separate, still-unpublished
|
||||
artifact at this checkpoint.
|
||||
|
||||
Producer, independent archive/server and root GUI evidence is preserved under
|
||||
`harness/experiments/authoring-prerelease-20260911/`. Full checks are described in
|
||||
the preparation, GUI and independent review reports. This working milestone
|
||||
satisfies the new AGENTS requirement to provide a runnable progress release;
|
||||
the full OA/SA product goal remains active and unaccepted.
|
||||
@@ -1,56 +0,0 @@
|
||||
# Independent authoring prerelease review — 2026-09-11
|
||||
|
||||
Codex-9e8r6t /root/behavior_audit. **PASS within the agreed archive/provenance/notices and packaged-server scope; no material finding.** Root owns packaged GUI acceptance and publication. This review does not establish completion of the full OA/SA release goal.
|
||||
|
||||
## Frozen artifact
|
||||
|
||||
Reviewed the completed ZIP independently from a new extraction under `.local/authoring-prerelease-review-20260911/extracted/`. The producer's staging directory and ZIP were not modified.
|
||||
|
||||
- Version: `0.2.0-preview.authoring.20260911`, target `x86_64-pc-windows-gnullvm`.
|
||||
- ZIP: `.local/authoring-prerelease-20260911/packages/ArcadiaStudio-0.2.0-preview.authoring.20260911-x86_64-pc-windows-gnullvm.zip`.
|
||||
- Size: **32,618,262 bytes**; **559 files**.
|
||||
- ZIP SHA256: `664f30b6217689aca1492eb263b3d15777067d732fa890eeb028f435209dadfa`.
|
||||
- GUI SHA256: `d4d9c2bd937e301ff1009d7cb93a34ee5aa6dfc837ff7e87d468c85371b20c31`.
|
||||
- Server SHA256: `efeeb89c0d2e0f93df4472cb3e9f48b2b722a37ae1043c90a5a56f2e845fe665`.
|
||||
- `libunwind.dll` SHA256: `f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4`.
|
||||
|
||||
The first independent artifact gate passed at **12:33:36–12:34:20 UTC**, with 3,643 assertions, including per-file checks. ZIP CRCs, safe ordinary members, case uniqueness, fixed timestamps, complete extracted membership and bytes all passed. `SHA256SUMS.txt` covers exactly the other 558 files. The 553 `package.json` input hashes match; the remaining six files are the explicit generated checksum, start instructions, third-party notice index, dependency inventory, documentation check and package metadata.
|
||||
|
||||
The only directory payloads are `deployment`, `docs`, `examples` and `licenses`; the root files are the declared executables/runtime, public license/readme and generated package records. Every shipped source document/example/deployment file and public license matched the isolated source. No harness, profile, store, credentials directory or source checkout was bundled. Local build paths appear in Cargo package identifiers and development-binary provenance; that is distinct from copying private files. This review does not claim a universal secret scan of binary strings.
|
||||
|
||||
## Source and notice correspondence
|
||||
|
||||
Independently read the original GUI manifest, Git blobs, retained server tar and packaged notice inventory, rather than adopting the producer's comparison booleans.
|
||||
|
||||
- All **253 original GUI input hashes** match auxiliary commit `234e01e27d6935d213097b345d91d369f808af82` and its isolated checkout. Base `2ec72d4241fa8971fa6fe9f626937de9dfd7c7eb` differs at exactly the seven documented LF/CRLF inputs. Each LF-to-CRLF conversion reproduces its original input hash; beyond those bytes the auxiliary diff is only `.gitattributes`.
|
||||
- All **120 originally recorded core/server files** match the preserved source tar, base Git blobs and isolated source bytes. Shared Cargo manifest/lock, three build files and raw OpenAPI bytes match the original GUI inputs and tar manifest. The tar SHA256 is `cbb371a93a562e1e641d7629fe351e016d63e66fc9112499b3fed6d5cd171f78`.
|
||||
- The inventory names **266 dependency packages**, with roots exactly `oa-desktop` and `oa-server`. Every reported package identity/registry checksum occurs in the historical lock. Every local crate's complete file set/hashes and every recorded common build input match the isolated source. Lock SHA256 remains `a6e070ed59b0ba19f18d60e8104494b777b470bd0268ae348cbe2b229790449f`.
|
||||
- All **484 distinct notice-reference files** match their inventory hashes, including Fira Sans, Iced Icons, Rust and libunwind notices. The one additional license is the LLVM-MinGW distribution license, SHA256 `8d85c1057d742e597985c7d4e6320b015a9139385cff4cbae06ffc0ebe89afee`, matching the recorded original. All **58 recorded compiler target-library hashes** match the retained toolchain files.
|
||||
|
||||
The package correctly describes its source snapshot as reconstructed input preservation, its executables as debug/development builds, and the original build records separately. Its metadata binds the exact executable hashes without claiming that packaging proves the caller's compiler/source/profile assertions. This reviewer did not rebuild the dependency graph or binaries; the producer's strict locked/offline metadata execution remains separately attributed. No reproducible-build, release-profile, universal-license-audit or Linux runtime claim is made. CLI is absent from this desktop/server package and is not advertised as included.
|
||||
|
||||
## Packaged server execution
|
||||
|
||||
The first runtime gate passed at **12:34:35–12:34:48 UTC**, with 1,800 assertions including complete pre/post package inventories. The executable ran only from the independent extraction. Its child environment contained `SystemRoot`, `WINDIR`, `PATH=C:\Windows\System32;C:\Windows`, and private `TEMP`/`TMP`; no toolchain path, token or proxy environment was inherited. All HTTP requests used a proxy-free loopback client. Data lived outside the package in `.local/authoring-prerelease-review-20260911/server-run/data`.
|
||||
|
||||
1. `--version` and `--help` exited successfully with empty stderr; the underlying application version is correctly `arcadia-server 0.2.0`.
|
||||
2. `/health` and `/health/ready` returned the exact expected success object. `/api/v1/openapi.json` matched the complete packaged OpenAPI bytes, including their recorded line endings.
|
||||
3. Exactly one POST created `Independent package persistence — 救助 20260911`. Complete response summary, distinct fresh resource/model UUIDs, Location and ETag matched the contract. Before execution, the checker predicted all empty-project fields and the full one-file `SourceBundle` from the published constructors/formatter. The subsequent GET and persisted disk envelope matched that complete expectation, allowing only the two freshly generated UUIDs.
|
||||
4. After the acknowledged durable create, the checker explicitly terminated its owned server process, started a second process on another private port using the same data directory, and repeated readiness/GET. Complete model/source, revision, ETag and every persisted envelope byte were unchanged. This is process-stop/restart persistence, not graceful-drain acceptance. Both owned processes were terminated and awaited.
|
||||
5. All **559 extracted package files** and the original ZIP were unchanged afterward.
|
||||
|
||||
The private listeners were `127.0.0.1:56455` (PID 8248) and `127.0.0.1:56463` (PID 1692). Resource `0517cc0a-c519-4e46-a1d5-2f385361552e` retained revision 1 and ETag `"p-0517cc0a-c519-4e46-a1d5-2f385361552e-r1"`. Durable envelope SHA256: `cf68e095e70717c7b86ec9e54c39f3a4b0effd331d748e1f4311592c7b8feb11`.
|
||||
|
||||
## Preserved evidence and limits
|
||||
|
||||
Private evidence remains under `.local/authoring-prerelease-review-20260911/`:
|
||||
|
||||
| Evidence | SHA256 |
|
||||
| --- | --- |
|
||||
| `review.py` | `af9eb20946dfcec78bf8959f1d5dc175e82ebb9fabba9f94cefe590fea516b92` |
|
||||
| `artifact-result.json` | `8b20e9d294338754ddc9b3320b59f55fc19ab979b8da18819fb47a8036db0bba` |
|
||||
| `server-result.json` | `086bf21e79f3bc7deddfd3278c8ade9b470b11031f7fb278ca0ead3a75b6cfb1` |
|
||||
|
||||
`server-run/` retains every HTTP response/body/header record, process stdout/stderr, original expected envelope and the private store. No artifact or runtime gate failed or required a retry. Earlier read-only schema inspection included a harmless list/dictionary inspection error; no executable had been launched and no artifact/evidence was overwritten.
|
||||
|
||||
No production/script changes, Cargo/rustc, GUI/native input, publication or credential access occurred in this review. Root's separate packaged GUI report and original named-creation native evidence must accompany any combined milestone acceptance. Current contextual-source, lifecycle, chooser and numerical work, full product parity and Linux verification retain their separate outstanding acceptance boundaries.
|
||||
@@ -1,62 +0,0 @@
|
||||
# Standalone CLI Windows packaging — 2026-09-11
|
||||
|
||||
Owner: Codex-9e8r6t /root/palette_finish. Root authorized implementation and isolated packaging; native acceptance, independent review, publication and credentials remain outside this worker's lease.
|
||||
|
||||
## Implementation and safety boundary
|
||||
|
||||
`scripts/package-cli-windows.ps1` creates a distinct `ArcadiaCLI-<version>-<target>.zip` from the prebuilt `arcadia.exe`. It requires an explicit build-evidence file and target, preserves the existing output-claim/no-overwrite behavior, checks the executable and runtime imports before staging, rechecks inspected hashes while copying, writes notices and checksums, and performs full deterministic ZIP membership/hash readback. Its returned acceptance status remains Pending.
|
||||
|
||||
The shared ordinary PE32+ reader, system-DLL allowlist, target/runtime selection and recursive ordinary-import check now live in `package-common.ps1`. The existing Studio packager has only the mechanical call-site replacement. Required files receive the existing ordinary-file/reparse check before inspection. Unknown imports, including unknown transitive runtime imports, still fail. Dynamic/delay imports remain a runtime-acceptance boundary. Common staging, notice, checksum and build-evidence helpers are reused; the new ZIP helper uses the existing reviewed Windows archive/readback algorithm.
|
||||
|
||||
`Get-ArcadiaNotices` adds only `oa-cli` to its allowed roots. Its default remains exactly `oa-desktop,oa-server`; the CLI entry point explicitly requests `oa-cli`. All locked/offline graph, local-source, crate archive, embedded-font, checksum-bound supplement, Rust and runtime requirements remain active. No registry dependencies or new license fallback were added.
|
||||
|
||||
The document helper accepts `--product cli`; omitted/default `studio` retains its original roots. CLI roots contain the CLI contract, language source/HTML, complete text-first example and complete linked release1 model/evidence. This avoids shipping the release1 walkthrough without the model it tells readers to copy. Relative document links are traversed and checked using the existing helper. The CLI README distinguishes packaged executable usage from source-checkout Cargo instructions and from Studio-specific example controls. No parser, core, CLI executable source, desktop behavior, common guide or grammar edits were made.
|
||||
|
||||
## Focused verification
|
||||
|
||||
Commands use the configured PowerShell and bundled Python, without Cargo or native application execution:
|
||||
|
||||
```powershell
|
||||
& scripts/test-package-cli-windows.ps1
|
||||
& scripts/test-windows-notices.ps1
|
||||
& 'C:\Users\User\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\python.exe' scripts/test-package-artifacts.py
|
||||
```
|
||||
|
||||
- **15 inert Windows/CLI assertions passed:** system imports, CLI-only binary set, missing Studio/server inputs, gnullvm runtime requirement, unsupported target, unknown direct/transitive imports, wrong PE architecture, runtime/license closure, build-evidence hash/target mismatch, exact deterministic ZIP, overwrite refusal preserving bytes, and input mutation after inspection.
|
||||
- **13 Python packaging tests passed**, including the original ELF/archive/document/notice invariants, explicit CLI transitive document closure and refusal, complete linked modular example selection, and exact default-versus-explicit Studio document roots. Initial run 0.954 seconds; after complete-example/readme refinement, final 1.252 seconds.
|
||||
- **Expanded existing notice suite passed**, including explicit CLI graph selection, unchanged desktop/server defaults and unknown-root rejection, together with prior normal/build/dev selection, Linux-server notices, runtime/source/font/supplement safety cases.
|
||||
- The extracted helper was also applied to the actual frozen D4D9 Studio, EFEE server and F456 runtime. Its complete import lists match the prior frozen Studio package metadata exactly.
|
||||
|
||||
Logs are under `.local/cli-packaging-20260911/`: `windows-initial.log`, `notices-initial.log`, `artifacts-initial.log`, `artifacts-final.log`. No failed compiler/package gate is hidden here; none has occurred at this checkpoint. The source diff's two whitespace-only helper lines were removed before staging.
|
||||
|
||||
## Exact source and artifact assembly
|
||||
|
||||
The semantic source basis is published commit `01b76a1e89e93b7992f39e63560aa8b2ca2f69e0`. An isolated checkout at `.local/cli-packaging-20260911/source` adds packaging-only auxiliary commits `8619708105c1851df58690c605001e22100a26ea` and `a0a48e82bd43be181caca1fe2c1514c21f2bc405`. The latter is the assembly basis, not an original compiler-input claim. Main-tree packaging edits remain for root integration; these private commits do not publish anything.
|
||||
|
||||
`prepare.py` independently verified six recorded CLI input hashes (root Cargo manifest/lock, CLI manifest, library, main and integration test), and 95 unchanged core/shared build input hashes. It copied and rehashed the frozen executable and runtime. `preparation.json` retains these input comparisons. Original evidence is `.local/source-cli-20260911/verified/manifest.json`; original build command was `cargo build --locked --offline -p oa-cli --bin arcadia`, successful at `2026-09-11T11:45:43.6035381Z`, duration 9.7336637 seconds. Prior author 12-test/strict-Clippy gates and independent 183 assertions/21 process invocations are recorded in their respective source-CLI reports, not rerun or reattributed by packaging.
|
||||
|
||||
| Input | Bytes | SHA256 |
|
||||
| --- | ---: | --- |
|
||||
| `.local/source-cli-20260911/verified/arcadia.exe` | 20312064 | `bad169f8be78dcfa74b32714569c886dd2132727080684fc2ba618d5822e3559` |
|
||||
| `.local/source-cli-20260911/verified/libunwind.dll` | 190976 | `f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4` |
|
||||
|
||||
Compiler evidence is Rust 1.98.1 commit `48a229ceaefd4985c50990b14116b6d856af0985`, Windows gnullvm, LLVM22.1.8; the frozen executable embeds that compiler path. Prior inventory compared the current toolchain and 58 target-library hashes. This is preserved development-profile output, not a release-profile or reproducible-build claim. `build-evidence.json` distinguishes the semantic source and later packaging assembly basis explicitly.
|
||||
|
||||
## Strict package and frozen handoff
|
||||
|
||||
After release_audit's final GUI/format gates terminated, it explicitly granted the metadata slot. The isolated invocation `.local/cli-packaging-20260911/package.ps1` passed on its first run, starting `2026-09-11T12:36:52.6900250Z`, finishing `12:37:33.7683084Z`, duration **41.084165 seconds**. No semantic binary build or native application execution occurred. The slot was explicitly released at terminal completion.
|
||||
|
||||
Frozen archive: `.local/cli-packaging-20260911/packages/ArcadiaCLI-0.2.0-preview.authoring.20260911-x86_64-pc-windows-gnullvm.zip`.
|
||||
|
||||
| Result | Measured value |
|
||||
| --- | --- |
|
||||
| ZIP bytes | **5091267** |
|
||||
| Package files | **114** |
|
||||
| SHA256 | **`2b051f8ce1ea4f59ee7e233e1fbd516d423aadbfbca5d549755cbe78fcde1af6`** |
|
||||
| Strict notice graph | **27 packages**, sole declared root `oa-cli` |
|
||||
| Original CLI lock hash | `2b960242c33eb839ff008d01731f6d5cdb4e047ca8cd6e13151cbf9c09913510` |
|
||||
| Checked local documentation links | **9** |
|
||||
| Full ZIP membership/hash readback | **PASS** |
|
||||
| Native acceptance returned by script | **Pending** |
|
||||
|
||||
`assembly.json` records final packaging-file hashes after the explicit-entry README correction and supersedes the initial packaging-file subset in `preparation.json`; both retain the original semantic input comparison. `package-initial.log`, `package-initial-result.json`, `build-evidence.json`, the package directory and sibling checksum retain the measured run. The ZIP and stage are frozen. Root and the independent reviewer received the exact identity and source boundary. Clean extracted CLI execution and independent review belong to that reviewer; publication belongs to root. Neither is claimed by this author result.
|
||||
@@ -1,53 +0,0 @@
|
||||
# Independent CLI packaging review — 2026-09-11
|
||||
|
||||
Codex-9e8r6t /root/behavior_audit. **PASS; no material finding in the bounded shared-script/default and packaged-CLI review.** Root owns integration and publication. No production edits, Cargo/rustc, GUI/native input or credentials were used.
|
||||
|
||||
## Source/default compatibility
|
||||
|
||||
Reviewed the nine packaging paths in auxiliary assembly `a0a48e82bd43be181caca1fe2c1514c21f2bc405` over semantic source `01b76a1e89e93b7992f39e63560aa8b2ca2f69e0`. The assembly diff contains exactly those nine script/readme/test paths; the reviewed main-tree files match their frozen assembly bytes. Individual hashes are in the private result.
|
||||
|
||||
- `Read-PackagePeImports` is byte-equivalent to its predecessor after function-name/line-ending normalization. The system DLL allowlist is unchanged. Required files now receive the shared ancestor-reparse check before inspection. Both the requested executable and packaged runtime are inspected, preserving refusal of unknown direct/transitive ordinary imports.
|
||||
- Studio still passes exactly `arcadia-studio.exe` and `arcadia-server.exe`. Its complete pipeline after input inspection is unchanged, including evidence, documents, notices, copying and archive handling. Linux packaging and both Docker files are unchanged.
|
||||
- Every original Python helper other than document product selection/argument dispatch is AST-equivalent, including ELF, tar, document-link and notice validation. Independently executed the old and new document selectors on the same frozen assembly: implicit Studio, explicit Studio and original behavior return exactly equal full file/source mappings for **Windows 65 files** and **Linux 64 files**. These counts describe that source snapshot, not the earlier Studio ZIP's document count.
|
||||
- The notice collector's only source delta is adding `oa-cli` to the root allowlist. Its signature still defaults to `oa-desktop,oa-server`; the rest of its body is unchanged. The CLI entry point explicitly selects only `oa-cli` and requires explicit build evidence.
|
||||
- The new Windows archive helper retains create-new/refusal behavior and full archive readback. CLI document roots include complete text-first and linked release1 examples. The CLI README correctly separates packaged executable usage, provisional preview identities, explicit writes and source-checkout Cargo instructions.
|
||||
|
||||
The author's separate 15 inert Windows assertions, 13 Python tests and expanded notice suite remain attributed to that worker's runs; they were not relabeled as independent executions here.
|
||||
|
||||
## Frozen archive and provenance
|
||||
|
||||
Freshly extracted `.local/cli-packaging-20260911/packages/ArcadiaCLI-0.2.0-preview.authoring.20260911-x86_64-pc-windows-gnullvm.zip` into the reviewer's separate `.local/cli-packaging-review-20260911/extracted/` directory.
|
||||
|
||||
| Artifact | Bytes | SHA256 |
|
||||
| --- | ---: | --- |
|
||||
| CLI ZIP | 5,091,267 | `2b051f8ce1ea4f59ee7e233e1fbd516d423aadbfbca5d549755cbe78fcde1af6` |
|
||||
| `arcadia.exe` | 20,312,064 | `bad169f8be78dcfa74b32714569c886dd2132727080684fc2ba618d5822e3559` |
|
||||
| `libunwind.dll` | 190,976 | `f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4` |
|
||||
|
||||
All **114 files**, ZIP CRCs, ordinary normalized members, fixed timestamps, extracted bytes and complete checksum inventory passed. All **108 package input hashes** match, with exactly six generated package records remaining. No Studio/server executable is included. Shipped docs/examples/public license and the CLI-specific README match the assembly bytes.
|
||||
|
||||
The **27 reported dependency package identities/checksums** agree with the historical CLI lock, whose SHA256 remains `2b960242c33eb839ff008d01731f6d5cdb4e047ca8cd6e13151cbf9c09913510`. Roots are exactly `oa-cli`. All **60 distinct notice-reference files** match their hashes; the runtime notice binds the shipped DLL. Complete local-crate and shared build inputs also match the inventory. The original CLI manifest's **six inputs** and **95 original core/shared build inputs** match the isolated source. These comparisons corroborate the declared preserved development build and later packaging-only assembly; they do not establish a reproducible build or independently rerun Cargo dependency resolution.
|
||||
|
||||
## Independent extracted CLI execution
|
||||
|
||||
The first gate passed **156 assertions across 10 subprocess invocations**, **12:42:56–12:43:12 UTC**. The absolute extracted executable ran with only package directory plus Windows/System32 on PATH, private TEMP/TMP, and SystemRoot/WINDIR. Tests used disposable example/source copies outside the package.
|
||||
|
||||
1. `--version` reported the underlying application version `arcadia 0.2.0`; `--help` listed the check, stabilization, semantic rename and preview/write contract.
|
||||
2. `check --json` accepted both complete packaged text-first and release1 examples without changing any copied example byte.
|
||||
3. A separately authored modular CRLF/Unicode fixture had three fixed IDs and one missing activity ID. Default stabilization preview left all physical files unchanged. Explicit stabilization wrote only the ID attached to the literal intended activity, preserving every other character, name, comment, reference and known detached file. Preview and write UUIDs were treated independently, as documented.
|
||||
4. Renaming `crew` to `rescue_team` first previewed without mutation, then changed exactly the actor declaration alias and typed allocation source. Quoted `@crew`, comments and display names remained unchanged. Complete expected file contents were specified independently; all four final IDs remained exact and ordered.
|
||||
5. Explicit no-op stabilization reported unchanged, preserved all bytes and retained every source file's modification timestamp. An explicit stabilization of invalid source exited 1 and preserved the original invalid bytes.
|
||||
6. All **114 extracted package files** and the frozen ZIP were unchanged after execution.
|
||||
|
||||
No gate failed or required a retry. This is package/runtime acceptance of the already preserved BAD169F8 CLI, not a rerun of the earlier 183-assertion semantic review, every source migration, cross-process conflict behavior, or Linux runtime acceptance.
|
||||
|
||||
## Evidence
|
||||
|
||||
Private `.local/cli-packaging-review-20260911/` retains the independent checker, structured source/artifact/runtime results, complete expected fixture texts and every subprocess stdout/stderr. Both JSON report exit codes and actual process exits were checked.
|
||||
|
||||
| Evidence | SHA256 |
|
||||
| --- | --- |
|
||||
| `review.py` | `f85d1f34ab75b4067efe09886e447bf9fd54dc232020bcb1a46404ec57b2351e` |
|
||||
| `result.json` | `3b92a816a62de386b5927b208c79c2a7c541e6e2e2e30bde31b4c11bbbc4509e` |
|
||||
|
||||
The generated activity identity is `9ba9e3cd-96f5-4f9a-865e-6b31ba94b4e6`; the result records its literal declaration binding and the three initially fixed IDs. Complete final physical source hashes are retained, including the unchanged detached file. No packaging staging directory, frozen artifact, common documentation, producer report or source was modified by this reviewer.
|
||||
@@ -1,67 +0,0 @@
|
||||
# Source-only CLI baseline — 2026-09-11
|
||||
|
||||
Codex-9e8r6t /root/palette_finish. Root authorized only new `crates/oa-cli`, its focused integration tests and this report. Root owns workspace membership, lockfile and public documentation. No grammar/schema/core/desktop change is part of this slice. The external source-language experiment remains a separate unratified design.
|
||||
|
||||
Read AGENTS, current issue1 through #143, existing local usability audit, external `release-textual-authoring-design-20260911.md`, and core source/session/file APIs. The independent reviewer supplied the loader's stabilized-warning/provisional-ID trap and verified journal/migration/no-op boundaries. Their review is `release-source-cli-review-20260911.md`.
|
||||
|
||||
## Proposed command contract implemented for validation
|
||||
|
||||
```
|
||||
arcadia check ROOT [--json]
|
||||
arcadia lint ROOT [--json]
|
||||
arcadia stabilize ROOT [--write] [--json]
|
||||
arcadia rename-alias ROOT OLD NEW [--write] [--json]
|
||||
```
|
||||
|
||||
ROOT is the explicit entry file. No project discovery, editor, server or network is required. Check and lint are equivalent admission plus engineering-diagnostic commands. A valid model with warnings returns success. Mutation commands preview by default; `--write` explicitly saves the candidate captured and displayed/reported by that same invocation. Missing IDs in an earlier separate preview are provisional: another invocation may allocate different IDs until saved. There is no persisted apply-plan protocol in this baseline.
|
||||
|
||||
Exit0 means valid/success (warnings permitted),1 means invalid source or rejected semantic alias command,2 means usage,3 means disk/path/conflict/pending journal or output failure. Pending-save diagnosis is obtained from the production loader's `pending_save` code, not a copied journal-name calculation. Invalid raw source is never migrated, stabilized or renamed.
|
||||
|
||||
Human output places located diagnostics/status/notes on stderr and per-file unified diffs on stdout. With `--write`, the human candidate is flushed before the saver is invoked. JSON output is a single versioned envelope containing diagnostics, status, exit code, write result, exact changed before/after text, unified diffs, missing-identity count and a separate structured source-migration report. JSON after a failed save retains the proposed diff with `written:false`; a failed save can require explicit recovery and is not a blanket rollback guarantee. Diagnostic locations are half-open UTF-8 offsets into the named coordinate space; input line/column are one-based Unicode scalar coordinates. Related locations carry exact byte spans. Command/disk errors without a responsible source token explicitly have no source location.
|
||||
|
||||
The raw `lint_bundle` diagnostics are authoritative for source positions. Loader-only discovery/IO/path/pending errors are retained; successful-loader warnings from stabilized compilation are discarded. Unpersisted lint object IDs are omitted so they cannot be mistaken for the later candidate's freshly allocated IDs. Explicit authored object IDs remain available. Post-compilation/update errors identify their candidate coordinate space rather than claiming to point into original bytes.
|
||||
|
||||
## API and safety boundary
|
||||
|
||||
`load_project` captures original byte expectations and discoverable source, then `lint_bundle` validates raw reachable text. On success, `compile_bundle` stabilizes identities and performs supported core migrations. Alias rename resolves the existing global alias and uses `prepare_command(SetAlias)` plus `update_bundle_with_identity_map`; it does not replace text blindly. The initial migration report is retained before the updater recompiles current source. The complete raw-to-final diff shows identity insertion, supported source migration and rename together while their summary fields remain distinct. There is no canonical formatter fallback or new file writer.
|
||||
|
||||
`Prepared` privately owns the original DiskProject and final CompiledSourceProject. Its consuming `write` invokes `source_files::save_project`, including for an empty diff. The saver rechecks pending journal and every captured dependency before its existing no-op return, preserving conflict and journal guarantees. No CLI path calls `recover_project`. The inherited contract is recoverability with entry committed last; it is not cross-process compare-and-swap, simultaneous multi-file visibility, or a Windows power-loss durability claim. Detached files are never deleted by this wrapper. No directories/files are created by a preview.
|
||||
|
||||
Admission limits match desktop policy:256 files, include depth32/edges1024,64MiB aggregate UTF-8,100000 graph objects,250000 records and500000 ordered references. These are exposed in help; no performance acceptance is inferred. The per-file diff uses a linear common-prefix/suffix algorithm with one hunk and three context lines, avoiding quadratic LCS memory on large files. Far-apart changes can produce a large hunk; JSON also preserves exact full changed-file texts and newline bytes.
|
||||
|
||||
Dependencies are existing `oa-core`, workspace `serde` and `serde_json`; no new registry package. Root added workspace membership, then granted the serialized Cargo window. Cargo.lock gained only the local `oa-cli`0.2.0 package and those three existing dependencies. Source formatting was limited to this crate.
|
||||
|
||||
## Focused acceptance and executed validation
|
||||
|
||||
Twelve integration tests cover the complete externally authored 40-identity alarm model; preview nonmutation and one-time exact ID-only stabilization; a same-invocation captured write; CRLF/comments/omitted defaults and no-op mtime/inventory; three-file semantic alias rename with independent complete-model alias-only expectation; invalid Unicode raw diagnostics and no writes; raw missing-ID warning locality/provisional-ID suppression; external change before write and before a no-op write; pending journal at load and after preview with untouched sentinel; rejected and same-alias rename; supported source5 migration separately reported through rename; and explicit root/machine-readable usage errors. Additional cases cover invalid UTF-8, unavailable includes, unsupported/invalid old source, non-element alias rename, a changed dependency absent from the proposed diff, and exact retention of a detached invalid file. Some tests contain several linked assertions, but this is twelve test functions, not an invented larger native acceptance count.
|
||||
|
||||
The complete example fixture is the frozen external authoring experiment, not a new CLI-generated model. No new syntax or broad text-usability acceptance is ratified. Large-model timing, persistent review-plan application, reparent/navigation/LSP, migration to a future grammar and publication packaging remain outside this baseline.
|
||||
|
||||
The Windows GNU/LLVM toolchain selected by `.local/toolchain/env.ps1`, existing target and `CARGO_BUILD_JOBS=1` ran these gates:
|
||||
|
||||
| Gate | Result |
|
||||
| --- | --- |
|
||||
| `cargo test --offline -p oa-cli --all-targets -- --test-threads=1` | Initial compilation succeeded;11/12 tests passed, exit101,58.621s total. The remaining fixture used unsupported `/* block comment */` syntax. |
|
||||
| `cargo test --locked --offline -p oa-cli --all-targets -- --test-threads=1` | After changing only that fixture to a supported line comment: **12/12 passed**, exit0,13.783s total /1.88s test execution. |
|
||||
| `cargo clippy --locked --offline -p oa-cli --all-targets -- -D warnings` | Passed,136.447s initially (including core compilation). Independent review then corrected a note that incorrectly described changed-file output as a complete project candidate. Final strict pass:10.245s. |
|
||||
| `cargo build --locked --offline -p oa-cli --bin arcadia` | Final wording-matched executable built successfully,9.734s. |
|
||||
| `rustfmt --check --edition 2024` on the three crate Rust files | Passed. |
|
||||
| Staged executable `--help` / `--version` | Successful; version `arcadia 0.2.0`. |
|
||||
|
||||
No production behavior was changed to accommodate the invalid block-comment fixture. The failed initial log remains preserved. The final note explicitly promises complete proposed **changed-file** text/diffs and disclaims a saved plan or digest of unchanged dependencies. Source-file expectations still capture every dependency, including those absent from the diff. No whole-project hash/apply protocol was added. The final wording-only change was compiled and strictly linted; the twelve semantic integration cases precede that wording change.
|
||||
|
||||
Immutable staging directory: `.local/source-cli-20260911/verified/`. Its `arcadia.exe` is **20312064 bytes**, SHA256 **`BAD169F8BE78DCFA74B32714569C886DD2132727080684FC2BA618D5822E3559`**. Sibling `libunwind.dll` is190976 bytes, SHA256 `F45604A12E8BCD839FD86F667C1BE1CC7C4F9DA2B3C69FF381AC708F0E1AE7D4`. `manifest.json` records source/workspace/lock/runtime/log identities; SHA256 `DD73FC2BB2190B23FFFE16D6C63D4ACDDF52338062FAC298379DDFD241D82EB8`. The matching test artifact is `.local/target-gnullvm/debug/deps/source_cli-343cec8034acf06e.exe`, SHA256 `F740CA81D0C60DEC4AA6A2BC36D3F7035844590FAB59E88F63A7F4ABE3F78AEF`.
|
||||
|
||||
Logs and separate exact command/exit/time JSON records remain under `.local/source-cli-20260911/`:
|
||||
|
||||
| Log | SHA256 |
|
||||
| --- | --- |
|
||||
| `tests-initial.log` | `00378D96D09A95265B09C9C7C12CB09A610E46055AC36FFD9ACEF3265965D56C` |
|
||||
| `tests-final.log` | `A18B708B89FE715951201D9046C414080E0820BDC9C6DC9EDE9CF3AB84E37C04` |
|
||||
| `clippy-initial.log` | `A3B5A7F051CF51B812BF5D4D0917E5CEFBD25CCCAD4033BAABD7EB36FCCFA72A` |
|
||||
| `clippy-final.log` | `29C23E6372447D06EF68A5B161F323F80109024CEE3BB7AEC85F421530BD1564` |
|
||||
| `build.log` | `B0968196245C5302F2576C087551E3F92071F85314E60512117BC632EED20742` |
|
||||
|
||||
Cargo lease was explicitly released to root/desktop owner after all gates terminated. Files remain uncommitted for root integration. The independent reviewer has the immutable executable for isolated no-compile falsifiers; that review is recorded separately and is not silently included in these author-run counts. No Linux CLI execution, native GUI or broad text-authoring release acceptance is claimed.
|
||||
|
||||
Independent review subsequently completed on the frozen BAD169F8 executable: **183 assertions across21 process invocations passed**, with no remaining implementation blocker. It covered all seven persistent identity families, exact header/live-reference changes with comment/quoted-alias retention and40 original UUIDs, saved/no-op source/mtime, raw CRLF/Unicode second-declaration warning offsets, explicit/provisional warning IDs and invalid non-BMP reference scalar columns. This is reviewer-owned evidence, not an addition to the author's12 tests: `harness/release-source-cli-review-20260911.md`, SHA256 `475645DB76CD193F2FC1CC1BDDAB57CCB0A724A62779A60C1E2AF0F756929344`; result `harness/experiments/source-cli-independent-20260911/result.json`, SHA256 `7F772FAB0E58C9901D9B87D7553C605C84E5DD2B234123C14EFD5B1BAF46DA9C`. The review discloses its corrected initial harness naming clash. Gitea issue1 comment176 records the handoff.
|
||||
@@ -1,136 +0,0 @@
|
||||
# Source CLI: independent contract review before implementation
|
||||
|
||||
Codex-9e8r6t `/root/behavior_audit`, 2026-09-11. Read-only review of the existing core and the proposed CLI contract; no production edits, Cargo, direct rustc, native input, or CLI test execution. `/root/palette_finish` owns `crates/oa-cli`. Root owns integration and release acceptance.
|
||||
|
||||
Reviewed AGENTS.md, issue #1 through #143, the empty open-PR list, Wiki `Authoring20260911`, and the imported `release-textual-authoring-design-20260911.md`. Inspected core is the working tree at HEAD `4bb4aaf3dc47b9738625b4d3f23ef8c74f286395`; this review did not inspect the author's evolving CLI implementation. References below are relative to the repository root. Existing tests are evidence of intended contracts, not newly executed results.
|
||||
|
||||
The proposed slice is coherent: explicit entry file; `check`/`lint` against original bytes; `stabilize` and semantic `rename-alias` preview by default; `--write` uses the same prepared candidate and the loader's original disk expectations. It does not select a new grammar, namespace scheme, identity policy, or source7 constructor defaults. The external design identifies this as useful immediate infrastructure, not completion of the text-only usability objective.
|
||||
|
||||
## Material traps
|
||||
|
||||
### 1. P1 — Loader diagnostics do not uniformly describe the raw files it returns
|
||||
|
||||
`source_files::discover_missing` retains original file text and bytes, then calls `compile_bundle` to populate `DiskProject.diagnostics` when discovery has no earlier diagnostic (`crates/oa-core/src/source_files.rs:528`). Compilation inserts missing UUIDs and reindexes the returned text (`source_project.rs:286`). Consequently, a successful load can return **raw files with warning spans from stabilized files**. Appending those warnings to raw lint output duplicates warnings and can point an editor beyond the intended declaration.
|
||||
|
||||
The existing counterexample is two missing-ID functions in one included file: a warning for the second function moves when the first UUID is inserted. `crates/oa-core/tests/source_project.rs:207` explicitly distinguishes these positions. Use `lint_bundle(snapshot.reachable_bundle(), limits)` as the semantic diagnostic authority for original bytes. Preserve loader-only discovery, encoding, path, and pending-save failures; replacing every loader diagnostic with lint would turn invalid UTF-8 into a less useful missing-file complaint or lose the journal refusal. Do not blindly reuse the loader's compiled semantic warnings.
|
||||
|
||||
There is a second identity dimension: `lint_bundle` calls `compile_once`, and `Parser::optional_id` allocates fresh UUIDs for omitted clauses (`language.rs:1226`). A raw lint warning's `object_id` can therefore differ from the later compiled candidate's UUID even though its raw position is correct. JSON must either omit such provisional identities, identify their provenance explicitly, or deliberately map them to the candidate. It must not imply that an independently allocated lint UUID identifies the record in the diff. Explicitly authored IDs remain stable.
|
||||
|
||||
The same separation applies to a failure during stabilization or migration after raw lint succeeds, such as the inserted IDs exceeding the source-byte budget. Its candidate-location diagnostic must not silently be presented as an original-buffer span. Spans are half-open UTF-8 byte offsets; line/column are one-based, with column counting Unicode scalar values (`language.rs:107`, `source_project.rs:640`).
|
||||
|
||||
Status: sent directly to the CLI author and root before implementation.
|
||||
|
||||
### 2. P1 — Keep the captured write baseline, including for no-op writes and failures
|
||||
|
||||
The correct write authority is the original `DiskProject.expectations`, which records bytes **before** stabilization (`source_files.rs:43`). Do not reload after preparation or replace those expectations with candidate bytes. Either would discard evidence of an intervening editor change. Check every captured dependency, including a file the proposed rename does not change.
|
||||
|
||||
An empty diff is not permission to skip `save_project`: the core checks the pending journal and all original expectations before its no-write return (`source_files.rs:880–902`). A prepared no-op followed by an external edit must refuse, rather than report an up-to-date successful write. A successful unchanged write should perform no file replacement and create no journal.
|
||||
|
||||
The core promises recoverability and checks for observed external changes, **not** a cross-process CAS, multi-file atomic visibility, or Windows power-loss durability (`source_files.rs:1–18`). A noncooperating writer can change a file between a check and its replacement. Different entry files can also have overlapping dependencies without sharing one journal. Neither this CLI nor its diff adds a stronger guarantee. No extra locking design is required for this bounded slice; describe the actual guarantee accurately.
|
||||
|
||||
Distinguish invalid-input refusal before saving from a disk failure during saving. `save_project` can have written files, attempted rollback, or left a pending journal when recovery cannot finish. Exit 3 must retain the specific conflict/recovery outcome and must not always claim that nothing was written. The saver may recover its own failed transaction internally; the CLI must not automatically recover a pre-existing transaction.
|
||||
|
||||
Status: author already proposed original expectations and no CAS claim; the empty-diff bypass and failed-write wording are explicit acceptance boundaries.
|
||||
|
||||
### 3. P2 — Preserve migration evidence before the rename updater removes it
|
||||
|
||||
`compile_bundle` can perform a targeted upgrade of accepted source3–6 in addition to UUID stabilization. The migration can add semantic defaults and generated interface allocations, not merely change a version token (`source_project/source_migration.rs:4–105`, `persistence.rs:15`). Capture this initial `decoded.migration` before applying `SetAlias`.
|
||||
|
||||
`update_bundle_with_identity_map` recompiles the already-upgraded bundle at `source_project.rs:1016`. The final schema7 result has no new migration report. Reading only `final_candidate.decoded.migration` would therefore hide the fact that a rename also upgraded the project. Preserve the initial report separately and show the complete original-to-final file diff, including generated declarations and defaults. The same-alias command may still change source through stabilization or migration; `changed` must compare original and final bytes, not only the semantic rename result.
|
||||
|
||||
The bundle parser explicitly admits source3–7 (`language.rs:1404`). Source1/2 migration belongs to the separate single-string legacy API; `tests/source_project.rs:605` explicitly proves those inputs are rejected by `compile_bundle`. Do not silently fall back to `language::parse` plus canonical export: that changes format, ownership, trivia, and the advertised command. State the supported input versions and retain unsupported/invalid raw files unchanged.
|
||||
|
||||
Source7 omitted defaults keep their existing meanings, including Scenario viewpoint `MixedLegacy`. CLI stabilization must not adopt a more helpful inferred viewpoint, allocation, parent, or occurrence order. Existing constructor-default probes and the external design are consistent on this boundary.
|
||||
|
||||
Status: sent directly to author/root; the author already intends explicit migration presentation.
|
||||
|
||||
### 4. P2 — A missing-ID preview is a proposal, not a persisted plan for a later process
|
||||
|
||||
Compiling the original missing-ID files twice allocates different UUIDs; the external design's executed probe already establishes this. Prepare the candidate once per invocation, and pass that exact model/bundle/index to saving. Do not regenerate a rename from raw files after displaying its diff or select the target by an ID allocated during the separate raw lint pass.
|
||||
|
||||
Resolve `OLD` by its exact stored alias in the compiled project's full identity namespace, then use `prepare_command(Command::SetAlias(...))` and its `identity_map` with the source updater. That namespace includes project, elements, relations, diagrams, placements, artifacts and traces; display names are not unique identity selectors (`model.rs:1727`, `session.rs:170,607,816`).
|
||||
|
||||
Separate preview and `--write` invocations may produce different *uncommitted* UUIDs. The author proposes disclosing this and deferring persisted plans, which is adequate for this slice. Do not describe `--write` as applying the previous invocation's exact patch. Once stabilized source has been written, repeated preparation must preserve all IDs. Never make name/path/content-derived IDs to conceal the preview difference.
|
||||
|
||||
For already stable source7, alias rename must change only the chosen stored alias and live reference tokens, preserving UUIDs, display names, comments, quoted prose, historical UUID literals, source membership and all other fields. Alias-like text inside a description is not a reference. The core updater already has the appropriate token behavior (`tests/source_project.rs:877`); do not implement a parallel text replacement algorithm.
|
||||
|
||||
Status: author explicitly accepted the per-invocation limitation and semantic updater path.
|
||||
|
||||
### 5. P2 — Journal and warning policy must survive the loader-to-CLI boundary
|
||||
|
||||
The existing loader appends a located `pending_save` diagnostic from the actual private journal path (`source_files.rs:534–547`). Use that diagnostic rather than recreating the journal naming convention. Loading itself is read-only but may compile internally before detecting the journal, so the accurate promise is refusal before the CLI prepares/adopts/writes a candidate, not detection before any load/compile work. The saver independently checks again. A pending journal remains a refusal even when source has no proposed change or contains a syntax error. Reserved stage/completed directories alone are not pending transactions.
|
||||
|
||||
The proposed policy—warnings exit 0, invalid source exit 1, usage exit 2, disk/conflict/journal refusal exit 3—matches core admission for structural errors versus engineering warnings (`session.rs:276`). Missing allocation, an empty scenario, or another allowed partial model must not become unsaveable merely because warnings exist. Conversely, raw invalid UTF-8/syntax, unresolved required references, unsupported versions, and path violations must never be replaced by a canonicalized or partially accepted bundle. Preserve their bytes and any pending journal. If an IO failure is represented as a discovery diagnostic, classify it consistently instead of assuming every diagnostic means exit 1.
|
||||
|
||||
`lint_bundle` may stop at the first syntax/conversion error, and structural-error admission returns errors rather than all engineering warnings (`source_project.rs:580–612`). A JSON result is not an exhaustive list of every defect; the CLI must not claim otherwise. Refactoring argument errors (absent source alias, invalid/reserved/colliding destination alias) likewise require a clear nonzero refusal with zero writes; distinguish command diagnostics from real source positions.
|
||||
|
||||
## Compact independent acceptance matrix
|
||||
|
||||
These are proposed CLI contract tests, not newly executed results. Compare complete `Project` and `SourceBundle` where a candidate exists, plus exact disk bytes for refusal/no-op cases.
|
||||
|
||||
| Case | Exact expected result |
|
||||
| --- | --- |
|
||||
| Raw diagnostics | Include a file with two missing-ID functions, CRLF and Unicode before a warning. Check/lint human and JSON locations select the original declaration; no stabilized warning duplicate or false candidate UUID. Explicit-ID version retains its real object identity. |
|
||||
| Invalid discovery and pending save | Invalid UTF-8 include, missing include, syntax failure, and real captured `pending_save` separately retain disk bytes and useful loader diagnostics. Pending save also refuses an otherwise no-op write; unrelated stage/completed directories do not imply pending recovery. A journal appearing after preparation is refused by saving. |
|
||||
| Missing-ID rename | Resolve an alias across includes, prepare once, and save that exact candidate. Readback is byte-for-byte candidate source and equal model with its exact IDs. A later fresh process can resolve the new alias. Two unwritten preparations may have different fresh IDs; the documented preview limitation is visible. |
|
||||
| Stable alias rename and rejection | Existing UUIDs, duplicate display names, comment/prose containing the old alias, an `@` reference separated by a comment, and a non-element alias target. Only intended alias/live reference tokens change. Unknown `OLD`, invalid/reserved/colliding `NEW` refuse without writing. |
|
||||
| Exact no-op and stale dependencies | Stable source7 stabilization and same-alias rename produce an empty diff and preserve exact bytes/line endings. Prepare the same no-op, edit or delete an unchanged dependency, then call the save boundary: exit 3 with no overwrite of the external bytes. |
|
||||
| Migration and admission | A valid old bundle with all UUIDs explicit still shows its migration and full diff for a real or same-alias rename. The report survives the updater. Source1/2 and invalid old-source drafts stay untouched. A candidate exceeding its budget after insertion is refused without a false raw location or any write. |
|
||||
| Warning-only acceptance | A permitted unallocated function or empty explicit-viewpoint scenario reports warnings and exits 0 for check/preview/write; acceptance does not invent allocations, change viewpoint, or populate steps. Structural errors exit 1 and preserve exact input. |
|
||||
| Detached disk files | A no-longer-reachable `.arcadia` file from a prior Undo remains byte-identical. The explicit entry's reachable source alone defines the model; do not recursively add or delete unrelated files. Ordinary exact no-op compares the reachable bundle while separately proving the retained disk file unchanged. |
|
||||
|
||||
No implementation blocker requires a core redesign. The actionable work is to preserve the distinct raw-diagnostic, compiled-candidate, original-disk-expectation, and migration-report contracts in the adapter. Root should request an independent implementation review after the CLI source freezes; this report is not CLI or release acceptance.
|
||||
|
||||
## Frozen implementation review and independent process evidence
|
||||
|
||||
Codex-9e8r6t `/root/behavior_audit`, 2026-09-11. This section supersedes only the earlier **unreviewed implementation** status. Reviewed the final `crates/oa-cli/src/lib.rs`, `src/main.rs`, all 12 author tests and their terminal logs, the relevant unchanged core parser/updater/saver paths, issue #1 through #170, Wiki `Authoring20260911`, and the empty open-PR list. Read the user's updated remote `AGENTS.md` blob `d93cddacdd45341abb7bbea1d3e3fd4493484d9e`, including coordinated milestone prereleases; root retains release ownership. No production edits, Cargo, rustc compilation, native input, commit, or push was performed for this review.
|
||||
|
||||
**Result: no remaining blocker found in this bounded CLI slice.** One presentation claim was corrected during review. The actual write and diagnostic boundaries preserve the prepared contract; broader text authoring and release acceptance remain separate.
|
||||
|
||||
### Corrected finding: P3 — describe the changed files, not a whole-candidate identity
|
||||
|
||||
The first candidate note said that the invocation's candidate was shown in full, while `Report.changes` contains only changed files. Two projects with different unchanged include bytes can therefore yield identical changed-file output. This is expected for a diff report, but the note overstated what it identified. The author corrected `lib.rs:353` to describe complete proposed **changed-file** texts/diffs and explicitly disclaim a saved plan or unchanged-dependency digest. Root confirmed that no persisted-plan or digest protocol was selected; this review does not require one.
|
||||
|
||||
Unchanged dependencies are still protected when saving. `Prepared` keeps the original `DiskProject` and compiled candidate in private fields; `Prepared::write` always calls `source_files::save_project` (`lib.rs:179–205`). It has no empty-diff bypass and does not reload or substitute expectations. The core checks all captured expectations (`source_files.rs:799–816,884`) and the actual journal before its no-write return at 902. Thus an include absent from `changes` is not absent from the write precondition. The author separately exercised a changed unchanged include and a captured no-op changed after preparation; those are author test results, not independent runtime claims from this review. No cross-process CAS guarantee is added.
|
||||
|
||||
### Other contracts traced to their actual implementation
|
||||
|
||||
- **Raw diagnostics and identities:** loader-only discovery/encoding/path/journal diagnostics survive; raw `lint_bundle` supplies semantic spans; stabilized loader warnings are filtered (`lib.rs:224–270`). Successful raw warnings retain object IDs only when those IDs were authored in the input. Invalid raw text returns before declaration analysis/updating, and later candidate failures explicitly say `candidate`. Independent process probes below exercise the shifted-second-declaration and non-BMP-column counterexamples.
|
||||
- **Semantic alias coverage and source preservation:** target resolution uses the compiled project's complete identity namespace, then `prepare_command(SetAlias)` and `update_bundle_with_identity_map` (`lib.rs:307–338`). It never uses an independently allocated lint UUID as its target. The core covers project, element, relation, diagram, placement, artifact and trace identities. Independent exact-source probes cover all seven, with comments and quoted old aliases deliberately retained.
|
||||
- **Migration/default boundary:** the adapter captures the initial migration report before the semantic updater (`lib.rs:305`), compares original raw text with the final candidate, and has no canonical-export, legacy-parser, inferred-viewpoint or implicit-allocation fallback. Bundle admission remains source3–7; source1/2 are not silently converted. The author's source5 rename test verifies migration retention and original-to-final diff. This review inspected the other admitted-version branches but did not independently execute a full source3–7 constructor matrix.
|
||||
- **Refusal and output:** `--write` consumes the same in-process candidate. Invalid source/commands use exit 1, usage 2, and path/conflict/journal/output failures 3; engineering warnings remain exit 0. A detected pending save is authoritative even when the source is invalid or the proposed write is unchanged. The CLI does not call `recover_project`. Human mode flushes its preview before saving; JSON returns the operation's final report. Failure wording correctly avoids equating `written=false` with guaranteed rollback. Separate missing-ID invocations remain distinct uncommitted proposals, explicitly disclosed.
|
||||
|
||||
### Independently executed frozen-binary probes
|
||||
|
||||
Ran the author's immutable Windows executable `.local/source-cli-20260911/verified/arcadia.exe`, 20,312,064 bytes, SHA-256 `BAD169F8BE78DCFA74B32714569C886DD2132727080684FC2BA618D5822E3559`, with its supplied `libunwind.dll`. The executable hash was checked before and after the run. No compiler was used.
|
||||
|
||||
Command:
|
||||
|
||||
```powershell
|
||||
& harness/experiments/source-cli-independent-20260911/probe.ps1 -Repository 'C:\Users\User\Documents\Git\ARCADIA-Studio'
|
||||
```
|
||||
|
||||
**183 assertions passed across 21 fresh CLI invocations.** The script computes every expected alias edit before the first mutation and writes only isolated `.local/source-cli-independent-20260911/verified/` fixture files. It refuses to overwrite a prior run directory; a new `-RunName` allows a separate repeat. Evidence lives in `harness/experiments/source-cli-independent-20260911/`: `probe.ps1`, `alias-input.arcadia`, `alias-expected-final.arcadia`, and `result.json` with individual assertions, commands, outcome counts and hashes.
|
||||
|
||||
| Independent case | Observed result |
|
||||
| --- | --- |
|
||||
| All seven identity families | Project `alarm_response`, element `maintenance`, relation `alarm_message`, diagram `response_view`, placement `detector_card`, artifact `response_procedure`, and trace `assessment_refinement` each previewed and wrote exactly the precomputed declaration/live-reference token changes. All 40 existing UUIDs, display names, other fields, comments, quoted aliases and source ownership remained byte-exact under the independent expectation. |
|
||||
| Preview, repeat invocation and stable no-op | Every preview left its input unchanged; each separate write of already-stable input matched its predicted preview exactly. Fresh final check succeeded. Stabilization then returned `unchanged`, no diff and `written=false`, preserving bytes and modification time and leaving no unexpected physical file. |
|
||||
| Raw warning position versus stabilized candidate | Two missing-ID SA functions in one CRLF include with Cyrillic, CJK and non-BMP text produced exactly two raw `unallocated_leaf` warnings, without loader-warning duplicates or provisional IDs. The second candidate declaration genuinely shifted after UUID insertion; its reported warning remained at the original input byte offset. Explicit-ID variants retained the exact authored warning IDs. |
|
||||
| Invalid Unicode reference | A non-BMP symbol before an unresolved reference distinguished UTF-8 byte offsets, Unicode scalar columns and UTF-16 units. The write attempt reported the exact original reference position, exit 1, no changes, no ephemeral identity, and preserved the complete input bundle bytes. |
|
||||
|
||||
The first harness launch stopped before starting the CLI because PowerShell's built-in `cli` alias resolved to `Clear-Item` instead of the intended helper. With stop-on-error, the missing literal `check` path stopped that invocation before the isolated source argument was handled. The helper was renamed to `Invoke-ArcadiaProbe`; the final clean run above passed. This was a harness error, not a product failure. The initial isolated input directory is retained; no production or original example file was passed to that mistaken command.
|
||||
|
||||
The author's separate gate is **12/12 tests passed**, 1.88 seconds execution, plus strict all-target Clippy, formatting and matching build. I read their preserved initial 11/12 log: the failed fixture used unsupported block-comment syntax, then was corrected to supported line-comment syntax without a product change. These compiled gates remain attributed to `/root/palette_finish`.
|
||||
|
||||
This independent run proves exact source-byte predictions and process/report behavior for the stated cases. It does **not** claim an independent full-`Project` oracle, adversarial simultaneous-writer proof, exhaustive source-version/default coverage, Linux execution, native GUI usability, packaging or release acceptance. Those limits do not create an additional blocker for this bounded source CLI adapter.
|
||||
|
||||
Reviewed source fingerprints:
|
||||
|
||||
| File | SHA-256 |
|
||||
| --- | --- |
|
||||
| `crates/oa-cli/src/lib.rs` | `97BEBAB63B4A611A1D36AD0C20FE452CBE1985FE3A6FBD39C954603DA11410FC` |
|
||||
| `crates/oa-cli/src/main.rs` | `DE964F9B6C59C0F31C080AFBD3B31D442B07B1B44F6A187A248D3409C45D35FB` |
|
||||
| `crates/oa-cli/tests/source_cli.rs` | `A3A1B856C7A9FD3D80BC8074150A24FBB0EF0E9DDF736C7CEA036725FC3DC2B8` |
|
||||
| `crates/oa-core/src/source_files.rs` | `669491DBDDB8E8E6556161426DFFA1A49AC1C826D31020BFF0956D81A4D84683` |
|
||||
| `crates/oa-core/src/source_project.rs` | `1394370BCC5908873B830C2C11312E6E1611AF41F46C4F43CA0A6BCB713BB466` |
|
||||
@@ -81,33 +81,22 @@ def linked_path(origin, link):
|
||||
return '/'.join(parts)
|
||||
|
||||
|
||||
def document_inputs(repository, platform, product='studio'):
|
||||
def document_inputs(repository, platform):
|
||||
repository = Path(repository).resolve()
|
||||
raw = subprocess.check_output(['git', '-C', str(repository), 'ls-files', '-z'])
|
||||
tracked = set(raw.decode('utf-8').split('\0')) - {''}
|
||||
if product == 'studio':
|
||||
roots = ['LICENSE', 'docs/server.md', 'docs/api.rst', 'docs/api.html',
|
||||
'docs/language.rst', 'docs/language.html', f'docs/{platform}-packaging.md',
|
||||
'deployment/Dockerfile', 'deployment/Dockerfile.dockerignore',
|
||||
'deployment/arcadia-server.service']
|
||||
roots += sorted(name for name in tracked if name.startswith('examples/'))
|
||||
readme = 'scripts/package-readme.md'
|
||||
elif product == 'cli':
|
||||
roots = ['LICENSE', 'docs/source-cli.md', 'docs/language.rst', 'docs/language.html',
|
||||
'examples/text-first/README.md']
|
||||
# The text-first guide links the modular water-pressure walkthrough;
|
||||
# ship its whole model/evidence directory, not just its Markdown links.
|
||||
roots += sorted(name for name in tracked if name.startswith(('examples/text-first/', 'examples/release1/')))
|
||||
readme = 'scripts/package-cli-readme.md'
|
||||
else:
|
||||
raise ValueError(f'Unknown package product: {product}')
|
||||
roots = ['LICENSE', 'docs/server.md', 'docs/api.rst', 'docs/api.html',
|
||||
'docs/language.rst', 'docs/language.html', f'docs/{platform}-packaging.md',
|
||||
'deployment/Dockerfile', 'deployment/Dockerfile.dockerignore',
|
||||
'deployment/arcadia-server.service']
|
||||
roots += sorted(name for name in tracked if name.startswith('examples/'))
|
||||
# Two explicitly authored inputs can be packaged before their first commit.
|
||||
permitted = tracked | set(roots) | {readme, 'docs/linux-packaging.md', 'docs/windows-packaging.md'}
|
||||
permitted = tracked | set(roots) | {'scripts/package-readme.md', 'docs/linux-packaging.md', 'docs/windows-packaging.md'}
|
||||
def repository_file(name):
|
||||
candidate = ordinary(repository / name)
|
||||
candidate.resolve().relative_to(repository)
|
||||
return candidate
|
||||
sources = {'README.md': repository_file(readme)}
|
||||
sources = {'README.md': repository_file('scripts/package-readme.md')}
|
||||
for name in roots:
|
||||
sources[name] = repository_file(name)
|
||||
queue = list(sources)
|
||||
@@ -346,7 +335,6 @@ def main():
|
||||
docs = commands.add_parser('docs')
|
||||
docs.add_argument('--repository', required=True)
|
||||
docs.add_argument('--platform', choices=['windows', 'linux'], required=True)
|
||||
docs.add_argument('--product', choices=['studio', 'cli'], default='studio')
|
||||
check = commands.add_parser('check-docs')
|
||||
check.add_argument('--stage', required=True)
|
||||
elf = commands.add_parser('elf')
|
||||
@@ -361,7 +349,7 @@ def main():
|
||||
notices.add_argument('--target', required=True)
|
||||
args = parser.parse_args()
|
||||
if args.command == 'docs':
|
||||
result = document_inputs(args.repository, args.platform, args.product)
|
||||
result = document_inputs(args.repository, args.platform)
|
||||
elif args.command == 'check-docs':
|
||||
result = check_documents(args.stage)
|
||||
elif args.command == 'elf':
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
# Arcadia CLI portable Windows bundle
|
||||
|
||||
Extract the whole archive into a writable directory. Open a terminal there:
|
||||
|
||||
```powershell
|
||||
.\arcadia.exe --version
|
||||
.\arcadia.exe --help
|
||||
.\arcadia.exe check examples/text-first/project.arcadia
|
||||
```
|
||||
|
||||
Keep `libunwind.dll` beside `arcadia.exe` when included. The x86-64 gnullvm bundle
|
||||
uses the Windows Universal C Runtime. No compiler installation, desktop editor,
|
||||
network service or machine PATH change is needed to use this executable.
|
||||
|
||||
Read the [CLI command and diagnostic contract](docs/source-cli.md), the
|
||||
[text-first examples](examples/text-first/README.md) and the
|
||||
[language reference](docs/language.html) ([text source](docs/language.rst)).
|
||||
Copy a complete example folder into your own work directory before editing it.
|
||||
Supply the project entry file explicitly. Editing commands preview by default;
|
||||
`--write` saves the candidate prepared by that invocation. A later invocation
|
||||
may choose different provisional UUIDs for records whose IDs are still missing.
|
||||
|
||||
Some example walkthroughs describe Studio controls such as Apply and Save.
|
||||
For this CLI, edit the copied text in your editor, run `arcadia check` on its
|
||||
entry, review `arcadia stabilize project.arcadia`, then use
|
||||
`arcadia stabilize project.arcadia --write` to save
|
||||
missing identities. The full `examples/release1` model and evidence are included
|
||||
for studying modular source; running its visual walkthrough requires Studio.
|
||||
|
||||
The build instructions in the CLI documentation refer to a separate source
|
||||
checkout; this archive contains the executable and examples. It does not include
|
||||
repository sources needed for Cargo commands. The published milestone's source
|
||||
and exact build evidence are identified in its release record and `package.json`.
|
||||
|
||||
`SHA256SUMS.txt` records the package files. `THIRD-PARTY-NOTICES.txt`,
|
||||
`dependency-notices.json` and `licenses/` retain exact dependency, embedded-asset,
|
||||
Rust and runtime notices. Packaging integrity does not establish runtime
|
||||
acceptance; read the distributor's binary-specific acceptance record.
|
||||
@@ -1,75 +0,0 @@
|
||||
#Requires -Version 5.1
|
||||
# Package an already-built CLI. No builds, downloads, recovery or application launch.
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet('x86_64-pc-windows-msvc', 'x86_64-pc-windows-gnullvm')]
|
||||
[string]$Target,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidatePattern('^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$')]
|
||||
[string]$Version,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$BinaryDirectory,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$BuildEvidence,
|
||||
[string]$LlvmMinGWRoot,
|
||||
[string]$Python = 'python',
|
||||
[string]$NoticeManifest = (Join-Path $PSScriptRoot 'windows-notices/manifest.json'),
|
||||
[string]$OutputRoot = (Join-Path $PSScriptRoot '../dist')
|
||||
)
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repository = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
||||
$encoding = New-Object Text.UTF8Encoding($false)
|
||||
. (Join-Path $PSScriptRoot 'package-common.ps1')
|
||||
. (Join-Path $PSScriptRoot 'windows-notices.ps1')
|
||||
$windowsInputs = Get-PackageWindowsInputs $BinaryDirectory $Target $LlvmMinGWRoot @('arcadia.exe')
|
||||
$inputs = $windowsInputs.Inputs
|
||||
$inspectedHashes = $windowsInputs.InspectedHashes
|
||||
$evidence = Read-PackageBuildEvidence $BuildEvidence $Target $windowsInputs.BinaryHashes
|
||||
$documentation = Invoke-PackageHelper $Python @('docs', '--repository', $repository, '--platform', 'windows', '--product', 'cli')
|
||||
foreach ($entry in $documentation.files) { $inputs[$entry.path] = $entry.source }
|
||||
$notices = Get-ArcadiaNotices $repository $Target $NoticeManifest $LlvmMinGWRoot @('oa-cli')
|
||||
foreach ($name in $notices.Inputs.Keys) {
|
||||
if ($inputs.Contains($name)) { throw "Notice collides with package input: $name" }
|
||||
$inputs[$name] = $notices.Inputs[$name]
|
||||
$inspectedHashes[$name] = $notices.Hashes[$name]
|
||||
}
|
||||
$output = [IO.Path]::GetFullPath($OutputRoot)
|
||||
$packageName = "ArcadiaCLI-$Version-$Target"
|
||||
$stage = Join-Path $output $packageName
|
||||
$archive = "$stage.zip"
|
||||
$checksum = "$archive.sha256"
|
||||
$lockPath = "$stage.packaging.lock"
|
||||
foreach ($path in @($stage, $archive, $checksum, $lockPath)) {
|
||||
if (Test-Path -LiteralPath $path) { throw "Output already exists; choose a fresh version or output root: $path" }
|
||||
}
|
||||
foreach ($directory in @('docs', 'examples', 'deployment', 'crates', 'scripts')) {
|
||||
$sourceRoot = [IO.Path]::GetFullPath((Join-Path $repository $directory)) + [IO.Path]::DirectorySeparatorChar
|
||||
if (($stage + [IO.Path]::DirectorySeparatorChar).StartsWith($sourceRoot, [StringComparison]::OrdinalIgnoreCase)) { throw 'Output must not be inside a packaged source directory.' }
|
||||
}
|
||||
[void][IO.Directory]::CreateDirectory($output)
|
||||
$lock = [IO.File]::Open($lockPath, [IO.FileMode]::CreateNew, [IO.FileAccess]::Write, [IO.FileShare]::None)
|
||||
try {
|
||||
[void][IO.Directory]::CreateDirectory($stage)
|
||||
$inputHashes = Copy-PackageInputs $stage $inputs $inspectedHashes
|
||||
$metadata = [ordered]@{
|
||||
product = 'Arcadia CLI'; version = $Version; target = $Target
|
||||
acceptance = 'Packaging integrity only; clean-environment CLI verification required.'
|
||||
build = 'Prebuilt input; source/compiler/profile assertions are recorded, not inferred.'
|
||||
build_evidence = $evidence; imports = $windowsInputs.Imports; input_sha256 = $inputHashes
|
||||
}
|
||||
[IO.File]::WriteAllText((Join-Path $stage 'package.json'), ($metadata | ConvertTo-Json -Depth 14) + "`n", $encoding)
|
||||
Write-PackageNotices $stage $notices
|
||||
[IO.File]::WriteAllText((Join-Path $stage 'START-HERE.txt'), "Arcadia CLI portable Windows bundle`nOpen a terminal in this folder and run .\arcadia.exe --help.`nRead README.md and docs/source-cli.md. Keep any bundled runtime DLL beside arcadia.exe.`nNo compiler, desktop editor, network service or machine PATH change is required.`nCopy an example into your own work directory before editing it. Preview is the default; --write saves.`nPackaging integrity does not establish runtime acceptance; see package.json and release evidence.`n", $encoding)
|
||||
$documentCheck = Invoke-PackageHelper $Python @('check-docs', '--stage', $stage)
|
||||
[IO.File]::WriteAllText((Join-Path $stage 'documentation-check.json'), ($documentCheck | ConvertTo-Json) + "`n", $encoding)
|
||||
Write-PackageChecksums $stage
|
||||
$result = Write-PackageWindowsArchive $stage $archive $packageName
|
||||
[IO.File]::WriteAllText($checksum, "$($result.SHA256) $packageName.zip`n", $encoding)
|
||||
[pscustomobject]@{ Directory = $stage; Archive = $archive; SHA256 = $result.SHA256; Files = $result.Files; NativeAcceptance = 'Pending' }
|
||||
} finally {
|
||||
$lock.Dispose()
|
||||
# Keep claims and partial outputs. A retry must never overwrite an earlier attempt.
|
||||
}
|
||||
@@ -74,147 +74,3 @@ function Write-PackageChecksums([string]$Stage) {
|
||||
}
|
||||
[IO.File]::WriteAllText((Join-Path $Stage 'SHA256SUMS.txt'), ($lines -join "`n") + "`n", $encoding)
|
||||
}
|
||||
|
||||
function Require-PackageFile([string]$Path) {
|
||||
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { throw "Required file missing: $Path" }
|
||||
Assert-PackageOrdinaryFile $Path
|
||||
return (Get-Item -LiteralPath $Path).FullName
|
||||
}
|
||||
|
||||
# Read ordinary PE32+ imports without invoking a compiler or trusting PATH tools.
|
||||
# Dynamically loaded libraries and delay imports remain a separate runtime smoke gate.
|
||||
function Read-PackagePeImports([string]$Path) {
|
||||
$stream = [IO.File]::OpenRead($Path)
|
||||
$reader = New-Object IO.BinaryReader($stream)
|
||||
try {
|
||||
if ($reader.ReadUInt16() -ne 0x5a4d) { throw "Not a PE executable: $Path" }
|
||||
$stream.Position = 0x3c
|
||||
$pe = $reader.ReadUInt32()
|
||||
$stream.Position = $pe
|
||||
if ($reader.ReadUInt32() -ne 0x4550) { throw "Invalid PE signature: $Path" }
|
||||
if ($reader.ReadUInt16() -ne 0x8664) { throw "Expected x86_64 PE input: $Path" }
|
||||
$sectionCount = $reader.ReadUInt16()
|
||||
$stream.Position = $pe + 20
|
||||
$optionalSize = $reader.ReadUInt16()
|
||||
$optional = $pe + 24
|
||||
$stream.Position = $optional
|
||||
if ($reader.ReadUInt16() -ne 0x20b) { throw "Expected PE32+ input: $Path" }
|
||||
$stream.Position = $optional + 120
|
||||
$importRva = $reader.ReadUInt32()
|
||||
$sections = @()
|
||||
for ($index = 0; $index -lt $sectionCount; $index++) {
|
||||
$stream.Position = $optional + $optionalSize + $index * 40 + 8
|
||||
$virtualSize = $reader.ReadUInt32()
|
||||
$virtualAddress = $reader.ReadUInt32()
|
||||
$rawSize = $reader.ReadUInt32()
|
||||
$rawAddress = $reader.ReadUInt32()
|
||||
$sections += [pscustomobject]@{ Start = $virtualAddress; Size = [Math]::Max($virtualSize, $rawSize); Raw = $rawAddress }
|
||||
}
|
||||
function Rva-Offset([uint32]$Rva) {
|
||||
foreach ($section in $sections) {
|
||||
if ($Rva -ge $section.Start -and $Rva -lt ($section.Start + $section.Size)) {
|
||||
return [long]($section.Raw + $Rva - $section.Start)
|
||||
}
|
||||
}
|
||||
throw "Unmapped PE RVA in $Path"
|
||||
}
|
||||
if ($importRva -eq 0) { return }
|
||||
$descriptors = Rva-Offset $importRva
|
||||
for ($index = 0; $index -lt 4096; $index++) {
|
||||
$stream.Position = $descriptors + $index * 20
|
||||
$fields = @(0..4 | ForEach-Object { $reader.ReadUInt32() })
|
||||
if (@($fields | Where-Object { $_ -ne 0 }).Count -eq 0) { return }
|
||||
$stream.Position = Rva-Offset $fields[3]
|
||||
$name = New-Object Text.StringBuilder
|
||||
for ($character = 0; $character -lt 256; $character++) {
|
||||
$value = $reader.ReadByte()
|
||||
if ($value -eq 0) { break }
|
||||
[void]$name.Append([char]$value)
|
||||
}
|
||||
if ($character -eq 256) { throw "Unterminated PE import name: $Path" }
|
||||
$name.ToString().ToLowerInvariant()
|
||||
}
|
||||
throw "Unterminated PE import table: $Path"
|
||||
} finally {
|
||||
$reader.Dispose()
|
||||
}
|
||||
}
|
||||
|
||||
function Get-PackageWindowsInputs([string]$BinaryDirectory, [string]$Target, [string]$LlvmMinGWRoot, [string[]]$BinaryNames) {
|
||||
if ($Target -notin @('x86_64-pc-windows-msvc', 'x86_64-pc-windows-gnullvm')) { throw 'Unsupported Windows target.' }
|
||||
if ($BinaryNames.Count -eq 0 -or @($BinaryNames | Where-Object { $_ -notin @('arcadia.exe', 'arcadia-studio.exe', 'arcadia-server.exe') }).Count -gt 0) { throw 'Unsupported Windows package binary.' }
|
||||
# Windows system components only. Unknown imports fail, including VC redistributable DLLs.
|
||||
# Add a redistributable only with explicit provenance, license, and recursive import checks.
|
||||
$systemDlls = @(
|
||||
'kernel32.dll', 'kernelbase.dll', 'user32.dll', 'ole32.dll', 'oleaut32.dll',
|
||||
'bcrypt.dll', 'bcryptprimitives.dll', 'advapi32.dll', 'dwmapi.dll', 'gdi32.dll',
|
||||
'shell32.dll', 'imm32.dll', 'uxtheme.dll', 'ws2_32.dll', 'ntdll.dll', 'opengl32.dll',
|
||||
'ucrtbase.dll', 'msvcrt.dll', 'crypt32.dll', 'secur32.dll', 'shlwapi.dll',
|
||||
'comdlg32.dll', 'comctl32.dll', 'version.dll', 'winmm.dll', 'winhttp.dll',
|
||||
'iphlpapi.dll', 'userenv.dll', 'dbghelp.dll', 'dwrite.dll', 'd2d1.dll',
|
||||
'd3d11.dll', 'd3d12.dll', 'dxgi.dll', 'd3dcompiler_47.dll', 'propsys.dll',
|
||||
'powrprof.dll', 'setupapi.dll', 'cfgmgr32.dll', 'winspool.drv', 'wtsapi32.dll'
|
||||
)
|
||||
$inputs = [ordered]@{}
|
||||
foreach ($name in $BinaryNames) {
|
||||
$inputs[$name] = Require-PackageFile (Join-Path $BinaryDirectory $name)
|
||||
}
|
||||
if ($Target -eq 'x86_64-pc-windows-gnullvm') {
|
||||
if (-not $LlvmMinGWRoot) { throw 'gnullvm requires -LlvmMinGWRoot for its exact runtime and license.' }
|
||||
$inputs['libunwind.dll'] = Require-PackageFile (Join-Path $LlvmMinGWRoot 'x86_64-w64-mingw32/bin/libunwind.dll')
|
||||
$inputs['licenses/llvm-mingw-LICENSE.TXT'] = Require-PackageFile (Join-Path $LlvmMinGWRoot 'LICENSE.TXT')
|
||||
} elseif ($LlvmMinGWRoot) {
|
||||
throw 'MSVC packaging does not use LLVM-MinGW; omit -LlvmMinGWRoot.'
|
||||
}
|
||||
|
||||
$imports = [ordered]@{}
|
||||
$inspectedHashes = @{}
|
||||
foreach ($name in @($inputs.Keys | Where-Object { $_ -match '\.(exe|dll)$' })) {
|
||||
$inspectedHashes[$name] = (Get-FileHash -LiteralPath $inputs[$name] -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
$imports[$name] = @(Read-PackagePeImports $inputs[$name] | Sort-Object -Unique)
|
||||
foreach ($dependency in $imports[$name]) {
|
||||
$isSystem = $dependency -match '^(api-ms-win-|ext-ms-win-)[a-z0-9-]+\.dll$' -or $systemDlls -contains $dependency
|
||||
if (-not $isSystem -and -not $inputs.Contains($dependency)) {
|
||||
throw "Unpackaged runtime import '$dependency' in '$name'. Resolve its provenance and dependency closure before packaging."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$binaryHashes = [ordered]@{}
|
||||
foreach ($name in $BinaryNames) { $binaryHashes[$name] = $inspectedHashes[$name] }
|
||||
return [pscustomobject]@{ Inputs = $inputs; Imports = $imports; InspectedHashes = $inspectedHashes; BinaryHashes = $binaryHashes }
|
||||
}
|
||||
|
||||
function Write-PackageWindowsArchive([string]$Stage, [string]$Archive, [string]$PackageName) {
|
||||
Add-Type -AssemblyName System.IO.Compression
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
$zipStream = [IO.File]::Open($archive, [IO.FileMode]::CreateNew)
|
||||
$zip = New-Object IO.Compression.ZipArchive($zipStream, [IO.Compression.ZipArchiveMode]::Create)
|
||||
try {
|
||||
foreach ($file in Get-ChildItem -LiteralPath $stage -Recurse -File | Sort-Object FullName) {
|
||||
$relative = $file.FullName.Substring($stage.Length + 1).Replace('\', '/')
|
||||
$entry = $zip.CreateEntry("$packageName/$relative", [IO.Compression.CompressionLevel]::Optimal)
|
||||
$entry.LastWriteTime = [DateTimeOffset]::new(2000, 1, 1, 0, 0, 0, [TimeSpan]::Zero)
|
||||
$destination = $entry.Open()
|
||||
$source = $file.OpenRead()
|
||||
try { $source.CopyTo($destination) } finally { $source.Dispose(); $destination.Dispose() }
|
||||
}
|
||||
} finally { $zip.Dispose(); $zipStream.Dispose() }
|
||||
|
||||
$zip = [IO.Compression.ZipFile]::OpenRead($archive)
|
||||
try {
|
||||
$seen = New-Object 'Collections.Generic.HashSet[string]' ([StringComparer]::OrdinalIgnoreCase)
|
||||
foreach ($entry in $zip.Entries) {
|
||||
if (-not $seen.Add($entry.FullName)) { throw "Duplicate archive entry: $($entry.FullName)" }
|
||||
$relative = $entry.FullName.Substring($packageName.Length + 1)
|
||||
$source = $entry.Open()
|
||||
$sha = [Security.Cryptography.SHA256]::Create()
|
||||
try { $actual = [BitConverter]::ToString($sha.ComputeHash($source)).Replace('-', '').ToLowerInvariant() }
|
||||
finally { $sha.Dispose(); $source.Dispose() }
|
||||
$expected = (Get-FileHash -LiteralPath (Join-Path $stage $relative) -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
if ($actual -ne $expected) { throw "Archive mismatch: $relative" }
|
||||
}
|
||||
if ($seen.Count -ne @(Get-ChildItem -LiteralPath $stage -Recurse -File).Count) { throw 'Archive file count mismatch.' }
|
||||
} finally { $zip.Dispose() }
|
||||
return [pscustomobject]@{ SHA256 = (Get-FileHash -LiteralPath $Archive -Algorithm SHA256).Hash.ToLowerInvariant(); Files = $seen.Count }
|
||||
}
|
||||
|
||||
+103
-5
@@ -23,11 +23,109 @@ $repository = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
||||
$utf8 = New-Object Text.UTF8Encoding($false)
|
||||
. (Join-Path $PSScriptRoot 'package-common.ps1')
|
||||
|
||||
$windowsInputs = Get-PackageWindowsInputs $BinaryDirectory $Target $LlvmMinGWRoot @('arcadia-studio.exe', 'arcadia-server.exe')
|
||||
$inputs = $windowsInputs.Inputs
|
||||
$imports = $windowsInputs.Imports
|
||||
$inspectedHashes = $windowsInputs.InspectedHashes
|
||||
$binaryHashes = $windowsInputs.BinaryHashes
|
||||
function Require-File([string]$Path) {
|
||||
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { throw "Required file missing: $Path" }
|
||||
return (Get-Item -LiteralPath $Path).FullName
|
||||
}
|
||||
|
||||
# Read ordinary PE32+ imports without invoking a compiler or trusting PATH tools.
|
||||
# Dynamically loaded libraries and delay imports remain a separate runtime smoke gate.
|
||||
function Read-PeImports([string]$Path) {
|
||||
$stream = [IO.File]::OpenRead($Path)
|
||||
$reader = New-Object IO.BinaryReader($stream)
|
||||
try {
|
||||
if ($reader.ReadUInt16() -ne 0x5a4d) { throw "Not a PE executable: $Path" }
|
||||
$stream.Position = 0x3c
|
||||
$pe = $reader.ReadUInt32()
|
||||
$stream.Position = $pe
|
||||
if ($reader.ReadUInt32() -ne 0x4550) { throw "Invalid PE signature: $Path" }
|
||||
if ($reader.ReadUInt16() -ne 0x8664) { throw "Expected x86_64 PE input: $Path" }
|
||||
$sectionCount = $reader.ReadUInt16()
|
||||
$stream.Position = $pe + 20
|
||||
$optionalSize = $reader.ReadUInt16()
|
||||
$optional = $pe + 24
|
||||
$stream.Position = $optional
|
||||
if ($reader.ReadUInt16() -ne 0x20b) { throw "Expected PE32+ input: $Path" }
|
||||
$stream.Position = $optional + 120
|
||||
$importRva = $reader.ReadUInt32()
|
||||
$sections = @()
|
||||
for ($index = 0; $index -lt $sectionCount; $index++) {
|
||||
$stream.Position = $optional + $optionalSize + $index * 40 + 8
|
||||
$virtualSize = $reader.ReadUInt32()
|
||||
$virtualAddress = $reader.ReadUInt32()
|
||||
$rawSize = $reader.ReadUInt32()
|
||||
$rawAddress = $reader.ReadUInt32()
|
||||
$sections += [pscustomobject]@{ Start = $virtualAddress; Size = [Math]::Max($virtualSize, $rawSize); Raw = $rawAddress }
|
||||
}
|
||||
function Rva-Offset([uint32]$Rva) {
|
||||
foreach ($section in $sections) {
|
||||
if ($Rva -ge $section.Start -and $Rva -lt ($section.Start + $section.Size)) {
|
||||
return [long]($section.Raw + $Rva - $section.Start)
|
||||
}
|
||||
}
|
||||
throw "Unmapped PE RVA in $Path"
|
||||
}
|
||||
if ($importRva -eq 0) { return }
|
||||
$descriptors = Rva-Offset $importRva
|
||||
for ($index = 0; $index -lt 4096; $index++) {
|
||||
$stream.Position = $descriptors + $index * 20
|
||||
$fields = @(0..4 | ForEach-Object { $reader.ReadUInt32() })
|
||||
if (@($fields | Where-Object { $_ -ne 0 }).Count -eq 0) { return }
|
||||
$stream.Position = Rva-Offset $fields[3]
|
||||
$name = New-Object Text.StringBuilder
|
||||
for ($character = 0; $character -lt 256; $character++) {
|
||||
$value = $reader.ReadByte()
|
||||
if ($value -eq 0) { break }
|
||||
[void]$name.Append([char]$value)
|
||||
}
|
||||
if ($character -eq 256) { throw "Unterminated PE import name: $Path" }
|
||||
$name.ToString().ToLowerInvariant()
|
||||
}
|
||||
throw "Unterminated PE import table: $Path"
|
||||
} finally {
|
||||
$reader.Dispose()
|
||||
}
|
||||
}
|
||||
|
||||
# Windows system components only. Unknown imports fail, including VC redistributable DLLs.
|
||||
# Add a redistributable only with explicit provenance, license, and recursive import checks.
|
||||
$systemDlls = @(
|
||||
'kernel32.dll', 'kernelbase.dll', 'user32.dll', 'ole32.dll', 'oleaut32.dll',
|
||||
'bcrypt.dll', 'bcryptprimitives.dll', 'advapi32.dll', 'dwmapi.dll', 'gdi32.dll',
|
||||
'shell32.dll', 'imm32.dll', 'uxtheme.dll', 'ws2_32.dll', 'ntdll.dll', 'opengl32.dll',
|
||||
'ucrtbase.dll', 'msvcrt.dll', 'crypt32.dll', 'secur32.dll', 'shlwapi.dll',
|
||||
'comdlg32.dll', 'comctl32.dll', 'version.dll', 'winmm.dll', 'winhttp.dll',
|
||||
'iphlpapi.dll', 'userenv.dll', 'dbghelp.dll', 'dwrite.dll', 'd2d1.dll',
|
||||
'd3d11.dll', 'd3d12.dll', 'dxgi.dll', 'd3dcompiler_47.dll', 'propsys.dll',
|
||||
'powrprof.dll', 'setupapi.dll', 'cfgmgr32.dll', 'winspool.drv', 'wtsapi32.dll'
|
||||
)
|
||||
$inputs = [ordered]@{}
|
||||
foreach ($name in @('arcadia-studio.exe', 'arcadia-server.exe')) {
|
||||
$inputs[$name] = Require-File (Join-Path $BinaryDirectory $name)
|
||||
}
|
||||
if ($Target -eq 'x86_64-pc-windows-gnullvm') {
|
||||
if (-not $LlvmMinGWRoot) { throw 'gnullvm requires -LlvmMinGWRoot for its exact runtime and license.' }
|
||||
$inputs['libunwind.dll'] = Require-File (Join-Path $LlvmMinGWRoot 'x86_64-w64-mingw32/bin/libunwind.dll')
|
||||
$inputs['licenses/llvm-mingw-LICENSE.TXT'] = Require-File (Join-Path $LlvmMinGWRoot 'LICENSE.TXT')
|
||||
} elseif ($LlvmMinGWRoot) {
|
||||
throw 'MSVC packaging does not use LLVM-MinGW; omit -LlvmMinGWRoot.'
|
||||
}
|
||||
|
||||
$imports = [ordered]@{}
|
||||
$inspectedHashes = @{}
|
||||
foreach ($name in @($inputs.Keys | Where-Object { $_ -match '\.(exe|dll)$' })) {
|
||||
$inspectedHashes[$name] = (Get-FileHash -LiteralPath $inputs[$name] -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
$imports[$name] = @(Read-PeImports $inputs[$name] | Sort-Object -Unique)
|
||||
foreach ($dependency in $imports[$name]) {
|
||||
$isSystem = $dependency -match '^(api-ms-win-|ext-ms-win-)[a-z0-9-]+\.dll$' -or $systemDlls -contains $dependency
|
||||
if (-not $isSystem -and -not $inputs.Contains($dependency)) {
|
||||
throw "Unpackaged runtime import '$dependency' in '$name'. Resolve its provenance and dependency closure before packaging."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$binaryHashes = [ordered]@{}
|
||||
foreach ($name in @('arcadia-studio.exe', 'arcadia-server.exe')) { $binaryHashes[$name] = $inspectedHashes[$name] }
|
||||
$evidence = Read-PackageBuildEvidence $BuildEvidence $Target $binaryHashes
|
||||
$documentation = Get-PackageDocumentation $repository 'windows' $Python
|
||||
foreach ($name in $documentation.Keys) { $inputs[$name] = $documentation[$name] }
|
||||
|
||||
@@ -61,44 +61,6 @@ class PackageContracts(unittest.TestCase):
|
||||
self.assertEqual(archive.getmember('fixture/examples/temperature delta.txt').mode, 0o644)
|
||||
self.assertEqual(len(archive.getmembers()), 3)
|
||||
|
||||
def test_cli_document_closure_uses_own_readme_and_transitive_links(self):
|
||||
names = ['LICENSE', 'docs/source-cli.md', 'docs/language.rst', 'docs/language.html',
|
||||
'examples/text-first/README.md', 'examples/text-first/project.arcadia',
|
||||
'docs/linked.md', 'docs/linked.html', 'examples/release1/oa/module.arcadia']
|
||||
for name in names:
|
||||
self.file(name, b'fixture')
|
||||
self.file('scripts/package-cli-readme.md', b'[CLI](docs/source-cli.md)')
|
||||
self.file('docs/source-cli.md', b'[More](linked.md)')
|
||||
self.file('docs/linked.md', b'[HTML](linked.html)')
|
||||
with patch.object(packaging.subprocess, 'check_output', return_value='\0'.join(names).encode()):
|
||||
result = packaging.document_inputs(self.root, 'windows', 'cli')
|
||||
mapped = {entry['path']: entry['source'] for entry in result['files']}
|
||||
self.assertEqual(Path(mapped['README.md']).name, 'package-cli-readme.md')
|
||||
self.assertIn('docs/linked.html', mapped)
|
||||
self.assertIn('examples/text-first/project.arcadia', mapped)
|
||||
self.assertIn('examples/release1/oa/module.arcadia', mapped)
|
||||
self.assertNotIn('docs/server.md', mapped)
|
||||
self.file('docs/linked.md', b'[Missing](untracked.md)')
|
||||
with patch.object(packaging.subprocess, 'check_output', return_value='\0'.join(names).encode()):
|
||||
with self.assertRaisesRegex(ValueError, 'Untracked/missing'):
|
||||
packaging.document_inputs(self.root, 'windows', 'cli')
|
||||
|
||||
def test_default_document_roots_equal_explicit_studio(self):
|
||||
names = ['LICENSE', 'docs/server.md', 'docs/api.rst', 'docs/api.html',
|
||||
'docs/language.rst', 'docs/language.html', 'docs/windows-packaging.md',
|
||||
'deployment/Dockerfile', 'deployment/Dockerfile.dockerignore',
|
||||
'deployment/arcadia-server.service', 'examples/sample.arcadia']
|
||||
for name in names:
|
||||
self.file(name, b'fixture')
|
||||
self.file('scripts/package-readme.md', b'Studio fixture')
|
||||
with patch.object(packaging.subprocess, 'check_output', return_value='\0'.join(names).encode()):
|
||||
default = packaging.document_inputs(self.root, 'windows')
|
||||
explicit = packaging.document_inputs(self.root, 'windows', 'studio')
|
||||
with self.assertRaisesRegex(ValueError, 'Unknown package product'):
|
||||
packaging.document_inputs(self.root, 'windows', 'unknown')
|
||||
self.assertEqual(default, explicit)
|
||||
self.assertEqual({entry['path'] for entry in default['files']}, set(names) | {'README.md'})
|
||||
|
||||
def test_archive_refuses_overwrite_and_case_collisions(self):
|
||||
self.file('stage/a', b'first')
|
||||
archive = self.file('archive.tar.gz', b'preserve')
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
#Requires -Version 5.1
|
||||
# Inert PE fixtures and archive checks. No Cargo, builds or executable launch.
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Set-StrictMode -Version Latest
|
||||
. (Join-Path $PSScriptRoot 'package-common.ps1')
|
||||
$fixture = Join-Path ([IO.Path]::GetTempPath()) ('arcadia-cli-package-' + [guid]::NewGuid().ToString('N'))
|
||||
[void][IO.Directory]::CreateDirectory($fixture)
|
||||
$encoding = New-Object Text.UTF8Encoding($false)
|
||||
$checks = 0
|
||||
function Assert([bool]$Condition, [string]$Message) { if (-not $Condition) { throw $Message }; $script:checks++ }
|
||||
function Refuses([scriptblock]$Action, [string]$Pattern) {
|
||||
try { & $Action; throw 'Expected refusal' } catch { Assert ($_.ToString() -match $Pattern) "Wrong refusal: $_" }
|
||||
}
|
||||
function Write-Pe([string]$Path, [string]$Import = 'kernel32.dll', [uint16]$Machine = 0x8664) {
|
||||
[void][IO.Directory]::CreateDirectory([IO.Path]::GetDirectoryName($Path))
|
||||
$data = New-Object byte[] 1024
|
||||
$stream = [IO.MemoryStream]::new($data, $true)
|
||||
$writer = [IO.BinaryWriter]::new($stream)
|
||||
try {
|
||||
$writer.Write([uint16]0x5a4d)
|
||||
$stream.Position = 0x3c; $writer.Write([uint32]0x80)
|
||||
$stream.Position = 0x80; $writer.Write([uint32]0x4550); $writer.Write($Machine); $writer.Write([uint16]1)
|
||||
$stream.Position = 0x94; $writer.Write([uint16]0xf0)
|
||||
$stream.Position = 0x98; $writer.Write([uint16]0x20b)
|
||||
$stream.Position = 0x110; $writer.Write([uint32]0x1000)
|
||||
$stream.Position = 0x190; $writer.Write([uint32]512); $writer.Write([uint32]0x1000); $writer.Write([uint32]512); $writer.Write([uint32]512)
|
||||
$stream.Position = 524; $writer.Write([uint32]0x1040)
|
||||
$stream.Position = 576; $writer.Write([Text.Encoding]::ASCII.GetBytes($Import)); $writer.Write([byte]0)
|
||||
[IO.File]::WriteAllBytes($Path, $data)
|
||||
} finally { $writer.Dispose() }
|
||||
}
|
||||
$bin = Join-Path $fixture 'bin'
|
||||
$cli = Join-Path $bin 'arcadia.exe'
|
||||
Write-Pe $cli
|
||||
$inspected = Get-PackageWindowsInputs $bin x86_64-pc-windows-msvc '' @('arcadia.exe')
|
||||
Assert (($inspected.Imports['arcadia.exe'] -join ',') -eq 'kernel32.dll') 'System import parse failed'
|
||||
Assert ($inspected.Inputs.Count -eq 1) 'CLI inspection requires unrelated binaries'
|
||||
Refuses { Get-PackageWindowsInputs $bin x86_64-pc-windows-msvc '' @('arcadia-studio.exe', 'arcadia-server.exe') } 'Required file missing'
|
||||
Refuses { Get-PackageWindowsInputs $bin x86_64-pc-windows-gnullvm '' @('arcadia.exe') } 'requires -LlvmMinGWRoot'
|
||||
Refuses { Get-PackageWindowsInputs $bin unsupported '' @('arcadia.exe') } 'Unsupported Windows target'
|
||||
Write-Pe $cli 'unknown-runtime.dll'
|
||||
Refuses { Get-PackageWindowsInputs $bin x86_64-pc-windows-msvc '' @('arcadia.exe') } 'Unpackaged runtime import'
|
||||
Write-Pe $cli 'kernel32.dll' 0x14c
|
||||
Refuses { Get-PackageWindowsInputs $bin x86_64-pc-windows-msvc '' @('arcadia.exe') } 'Expected x86_64'
|
||||
Write-Pe $cli 'libunwind.dll'
|
||||
$runtime = Join-Path $fixture 'runtime'
|
||||
$dll = Join-Path $runtime 'x86_64-w64-mingw32/bin/libunwind.dll'
|
||||
Write-Pe $dll 'unknown-transitive.dll'
|
||||
[IO.File]::WriteAllText((Join-Path $runtime 'LICENSE.TXT'), 'Synthetic license', $encoding)
|
||||
Refuses { Get-PackageWindowsInputs $bin x86_64-pc-windows-gnullvm $runtime @('arcadia.exe') } 'Unpackaged runtime import'
|
||||
Write-Pe $dll
|
||||
$inspected = Get-PackageWindowsInputs $bin x86_64-pc-windows-gnullvm $runtime @('arcadia.exe')
|
||||
Assert ($inspected.Inputs.Count -eq 3) 'Runtime and license closure absent'
|
||||
$evidencePath = Join-Path $fixture 'evidence.json'
|
||||
$evidence = @{source_identity='test'; profile='inert'; compiler='none'; target='x86_64-pc-windows-gnullvm'; binary_sha256=@{'arcadia.exe'='wrong'}}
|
||||
[IO.File]::WriteAllText($evidencePath, ($evidence | ConvertTo-Json), $encoding)
|
||||
Refuses { Read-PackageBuildEvidence $evidencePath x86_64-pc-windows-gnullvm $inspected.BinaryHashes } 'binary hash mismatch'
|
||||
$evidence.binary_sha256['arcadia.exe'] = $inspected.BinaryHashes['arcadia.exe']
|
||||
[IO.File]::WriteAllText($evidencePath, ($evidence | ConvertTo-Json), $encoding)
|
||||
$null = Read-PackageBuildEvidence $evidencePath x86_64-pc-windows-gnullvm $inspected.BinaryHashes
|
||||
Refuses { Read-PackageBuildEvidence $evidencePath x86_64-pc-windows-msvc $inspected.BinaryHashes } 'target differs'
|
||||
$stage = Join-Path $fixture 'stage'
|
||||
[void][IO.Directory]::CreateDirectory($stage)
|
||||
$null = Copy-PackageInputs $stage $inspected.Inputs $inspected.InspectedHashes
|
||||
Write-PackageChecksums $stage
|
||||
$first = Write-PackageWindowsArchive $stage (Join-Path $fixture 'first.zip') 'same-name'
|
||||
$second = Write-PackageWindowsArchive $stage (Join-Path $fixture 'second.zip') 'same-name'
|
||||
Assert ($first.SHA256 -eq $second.SHA256 -and $first.Files -eq 4) 'ZIP is not deterministic/exact'
|
||||
Refuses { Write-PackageWindowsArchive $stage (Join-Path $fixture 'first.zip') 'same-name' } 'already exists'
|
||||
Assert ((Get-FileHash (Join-Path $fixture 'first.zip')).Hash.ToLowerInvariant() -eq $first.SHA256) 'Refusal changed archive'
|
||||
Write-Pe $cli 'kernelbase.dll'
|
||||
Refuses { Copy-PackageInputs (Join-Path $fixture 'changed-stage') $inspected.Inputs $inspected.InspectedHashes } 'Input changed after inspection'
|
||||
Write-Output "PASS: $checks CLI/Windows packaging assertions; inert fixture retained at $fixture"
|
||||
@@ -20,7 +20,7 @@ $null = Write-Fixture 'rust/share/doc/rust/COPYRIGHT.html' 'Synthetic Rust distr
|
||||
$null = Write-Fixture 'rust/share/doc/rust/COPYRIGHT-library.html' 'Synthetic Rust library notice'
|
||||
$null = Write-Fixture 'rust/lib/fake.rlib' 'Synthetic target library'
|
||||
$packages = @(); $nodes = @(); $lock = @()
|
||||
foreach ($name in @('oa-desktop', 'oa-server', 'oa-cli', 'normal', 'build', 'dev-only', 'unreachable')) {
|
||||
foreach ($name in @('oa-desktop', 'oa-server', 'normal', 'build', 'dev-only', 'unreachable')) {
|
||||
$external = $name -notlike 'oa-*'
|
||||
$root = if ($external) { "registry/src/test/$name-1.0.0" } else { "workspace/$name" }
|
||||
$manifest = Write-Fixture "$root/Cargo.toml" "[package]`nname = `"$name`"`nversion = `"1.0.0`""
|
||||
@@ -40,13 +40,10 @@ foreach ($name in @('oa-desktop', 'oa-server', 'oa-cli', 'normal', 'build', 'dev
|
||||
$deps += [pscustomobject]@{ pkg = $pair[0]; dep_kinds = @([pscustomobject]@{ kind = $pair[1] }) }
|
||||
}
|
||||
}
|
||||
if ($name -eq 'oa-cli') {
|
||||
$deps += [pscustomobject]@{ pkg = 'normal'; dep_kinds = @([pscustomobject]@{ kind = $null }) }
|
||||
}
|
||||
$nodes += [pscustomobject]@{ id = $id; deps = $deps; features = @() }
|
||||
}
|
||||
$null = Write-Fixture 'Cargo.lock' ($lock -join "`n")
|
||||
$script:FixtureMetadata = [pscustomobject]@{ packages = $packages; workspace_members = @('oa-desktop', 'oa-server', 'oa-cli'); resolve = [pscustomobject]@{ nodes = $nodes } }
|
||||
$script:FixtureMetadata = [pscustomobject]@{ packages = $packages; workspace_members = @('oa-desktop', 'oa-server'); resolve = [pscustomobject]@{ nodes = $nodes } }
|
||||
$script:FixtureTarget = 'x86_64-pc-windows-msvc'
|
||||
function cargo {
|
||||
if (($args -join ' ') -notmatch '--locked --offline' -or ($args -join ' ') -notmatch "--filter-platform $script:FixtureTarget") { throw 'Unexpected Cargo command' }
|
||||
@@ -63,12 +60,6 @@ function rustc {
|
||||
$manifestPath = Write-Fixture 'supplements/manifest.json' '{"packages":[],"assets":[],"runtimes":[]}'
|
||||
function Collect { Get-WindowsNotices $fixture x86_64-pc-windows-msvc $manifestPath '' }
|
||||
function Assert([bool]$Condition, [string]$Message) { if (-not $Condition) { throw $Message } }
|
||||
$defaultRoots = (Collect).Inventory.root_packages -join ','
|
||||
Assert ($defaultRoots -eq 'oa-desktop,oa-server') 'Desktop/server default roots changed'
|
||||
$cliOnly = Get-ArcadiaNotices $fixture x86_64-pc-windows-msvc $manifestPath '' @('oa-cli')
|
||||
Assert (($cliOnly.Inventory.root_packages -join ',') -eq 'oa-cli') 'CLI root declaration missing'
|
||||
Assert ((@($cliOnly.Inventory.packages.name | Sort-Object) -join ',') -eq 'normal,oa-cli') 'CLI notice graph leaked desktop/server/dev dependencies'
|
||||
try { $null = Get-ArcadiaNotices $fixture x86_64-pc-windows-msvc $manifestPath '' @('unknown'); throw 'Expected root refusal' } catch { Assert ($_.ToString() -match 'Notice roots must') 'Unknown root was not rejected before metadata' }
|
||||
function Refuses([string]$Pattern) {
|
||||
try { $null = Collect; throw 'Expected preflight refusal' } catch {
|
||||
if ($_.Exception.Message -notmatch $Pattern) { throw }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#Requires -Version 5.1
|
||||
# Read-only dependency preflight. Does not build, download, or change a Cargo target.
|
||||
function Get-ArcadiaNotices([string]$Repository, [string]$Target, [string]$NoticeManifest, [string]$LlvmMinGWRoot, [string[]]$RootPackages = @('oa-desktop', 'oa-server')) {
|
||||
if ($RootPackages.Count -eq 0 -or @($RootPackages | Where-Object { $_ -notin @('oa-desktop', 'oa-server', 'oa-cli') }).Count -gt 0) { throw 'Notice roots must be oa-desktop, oa-server and/or oa-cli.' }
|
||||
if ($RootPackages.Count -eq 0 -or @($RootPackages | Where-Object { $_ -notin @('oa-desktop', 'oa-server') }).Count -gt 0) { throw 'Notice roots must be oa-desktop and/or oa-server.' }
|
||||
if ($LlvmMinGWRoot -and $Target -ne 'x86_64-pc-windows-gnullvm') { throw 'LLVM-MinGW runtime notices apply only to the gnullvm target.' }
|
||||
function Hash-File([string]$Path) {
|
||||
$stream = [IO.File]::OpenRead($Path)
|
||||
|
||||
Reference in New Issue
Block a user